package com.qd.panda.service.stroke;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.math.Money;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.core.weixinmanager.model.MiddleUser;
import com.cdqidi.core.weixinmanager.service.MiddleUserService;
import com.cdqidi.dto.ApiResult;
import com.cdqidi.dto.ApiResultWrapper;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.DateUtil;
import com.cdqidi.util.RedisTemplateUtil;
import com.cdqidi.util.SpringContextHolder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.qd.common.panda.config.PandaParkConfigDTO;
import com.qd.common.panda.constant.PandaConstant;
import com.qd.common.panda.domain.entity.carpark.CarParkInfoDTO;
import com.qd.common.panda.domain.entity.carpark.CarParkLogDTO;
import com.qd.common.panda.domain.entity.carpark.PersonCarPark;
import com.qd.common.panda.domain.entity.rule.BaseRuleDTO;
import com.qd.common.panda.domain.entity.shop.order.ProductOrderDTO;
import com.qd.common.panda.domain.entity.stroke.*;
import com.qd.common.panda.domain.entity.stroke.mystroke.RunMyStrokeVO;
import com.qd.common.panda.domain.entity.stroke.mystroke.UserChargeStrokeVO;
import com.qd.common.panda.domain.entity.stroke.mystroke.UserParkStrokeVO;
import com.qd.common.panda.domain.entity.stroke.mystroke.UserStrokeVO;
import com.qd.common.panda.event.GasEvent;
import com.qd.common.sys.util.SecurityUtils;
import com.qd.panda.dc.DcService;
import com.qd.panda.dc.dto.ChargeStatusDTO;
import com.qd.panda.dc.dto.PayInfoDTO;
import com.qd.panda.dc.dto.charge.ChargeActionReqDTO;
import com.qd.panda.dc.dto.charge.StartChargeRespDTO;
import com.qd.panda.dc.dto.charge.StatusChargeRespDTO;
import com.qd.panda.model.*;
import com.qd.panda.service.AsyncService;
import com.qd.panda.service.card.UserCardService;
import com.qd.panda.service.hk.HkService;
import com.qd.panda.service.mall.MallService;
import com.qd.panda.service.org.OrgAgentService;
import com.qd.panda.service.park.*;
import com.qd.panda.service.plate.UserPlateRelationService;
import com.qd.panda.service.rule.BillRuleSetService;
import com.qd.panda.service.shop.order.ProductOrderService;
import com.qd.panda.service.space.ParkSpaceSyncService;
import com.qd.panda.service.stroke.delay.UserStrokeDelayDTO;
import com.qd.panda.service.stroke.order.GasOrderService;
import com.qd.panda.service.stroke.order.UserStrokeOrderChargeService;
import com.qd.panda.service.stroke.order.UserStrokeOrderRefundService;
import com.qd.panda.service.stroke.order.UserStrokeOrderService;
import com.qd.panda.service.third.fruit.FruitService;
import com.qd.panda.service.wallet.user.WalletHolderService;
import com.qd.pandaparkflow.bean.PriceReqVO;
import com.qd.pandaparkflow.context.PriceContext;
import com.qd.pay.config.PayConstant;
import com.qd.pay.domain.CloseOrderDTO;
import com.qd.pay.domain.OrderDTO;
import com.qd.pay.domain.OrderRefundDTO;
import com.qd.pay.service.order.OrderHolderService;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static java.util.stream.Collectors.toList;

@Component
@RequiredArgsConstructor
@Slf4j
public class UserStrokeHolderService {
    public static final String DELAY_QUEUE_NAME = "user_stroke_delay";
    private static final String ORDER_ID = "orderId";
    public static final String AMOUNT = "amount";
    private static final String NULL_PARK_ID_MSG = "停车场ID不存在";
    private static final String NULL_SID_MSG = "行程不存在";
    public static final String REDIS_LOCKED_ERR = "获得redis锁超时";
    private final ParkCacheService parkCacheService;
    private final CarParkAppointmentSetService carParkAppointmentSetService;
    private final CarParkService carParkService;
    private final CarParkFeeService carParkFeeService;
    private final CarParkDoorpostLoginService doorpostLoginService;
    private final CarParkLogService carParkLogService;
    private final ParkStrokeHolderService parkStrokeHolderService;

    private final UserStrokeService userStrokeService;
    private final UserPlateRelationService userPlateRelationService;
    private final UserStrokeHistoryService userStrokeHistoryService;
    private final UserStrokeOrderService userStrokeOrderService;
    private final UserStrokeOrderChargeService userStrokeOrderChargeService;
    private final UserStrokeConfirmService userStrokeConfirmService;
    private final UserStrokeOrderRefundService userStrokeOrderRefundService;
    private final UserStrokeAppointmentOrderService userStrokeAppointmentOrderService;
    private final UserStrokeProcessDetailService userStrokeProcessDetailService;
    private final HkService hkService;

    private final OrderHolderService orderHolderService;
    private final BillRuleSetService billRuleSetService;
    private final OrgAgentService orgAgentService;

    private final ObjectMapper objectMapper;
    @Autowired(required = false)
    private FlowExecutor flowExecutor;
    @Resource
    private MiddleUserService middleUserService;

    private final PandaParkConfigDTO pandaParkConfig;

    private final AsyncService asyncService;

    private final DcService dcService;
    private final RedissonClient redissonClient;

    private final StringRedisTemplate stringRedisTemplate;

    private final UserCardService userCardService;

    private final ProductOrderService productOrderService;
    private final FruitService fruitService;

    private final ParkSpaceSyncService parkSpaceSyncService;

    private final WalletHolderService walletHolderService;

    private final MallService mallService;

    /**
     * 预约
     *
     * @param dto 预约信息
     * @return 成功 or 失败
     */
    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> reserve(ReserveDTO dto) {
        checkCarPark(dto.getParkId());
        checkPlateNumber(dto.getPlateNumber());
        CarParkAppointmentSet carParkAppointmentSet = checkAppointment(dto);
        if (dto.getStrongReservation().equals(PandaConstant.STRONG_RESERVATION_1)) {
            BigDecimal amount = carParkAppointmentSet.getAmount();
            if (null != amount && amount.signum() == BigInteger.ONE.signum()) {
                return ApiResult.error("强预约需要支付订金，该接口不支持");
            }
        }
        //开始预约,强预约和弱预约金额都是0，区别是强预约有订单，保留车位
        Map<String, String> param = createUserStroke(dto);
        dto.freeData();
        carParkAppointmentSet.freeData();
        return ApiResult.success(param);
    }

    /**
     * 预约支付，只有强预约且预约费用大于0的
     *
     * @param dto 预约信息
     * @return 支付参数
     */
    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> reservePay(ReserveDTO dto) {
        String parkId = dto.getParkId();
        String plateNumber = dto.getPlateNumber();
        String userId = SecurityUtils.getLoginUser().getUserId();
        checkCarPark(dto.getParkId());
        checkPlateNumber(plateNumber);
        CarParkAppointmentSet carParkAppointmentSet = checkAppointment(dto);
        if (carParkAppointmentSet.getAmount().signum() < 1) {
            return ApiResult.error("本接口只针对需要支付预约费用的停车场");
        }
        String orderId = IdWorker.getIdStr();
        String sid = IdWorker.getIdStr();

        UserStrokeAppointmentOrderDTO userStrokeAppointmentOrder = new UserStrokeAppointmentOrderDTO();
        userStrokeAppointmentOrder.setUserId(userId);
        userStrokeAppointmentOrder.setOrderId(orderId);
        userStrokeAppointmentOrder.setSid(sid);
        userStrokeAppointmentOrder.setAppointmentTime(LocalDateTime.now());
        userStrokeAppointmentOrder.setParkId(parkId);
        userStrokeAppointmentOrder.setPlateNumber(plateNumber);
        userStrokeAppointmentOrder.setOrderAmount(carParkAppointmentSet.getAmount());
        userStrokeAppointmentOrder.setPayStatus(DisplayUtil.PAY_STATUS_0);
        userStrokeAppointmentOrder.setTripType(0);
        final String promotionCode = SecurityUtils.getLoginUser().getUser().getAppUserExtend().getPromotionCode();
        if (StringUtils.hasLength(promotionCode)) {
            userStrokeAppointmentOrder.setPromotionCode(promotionCode);
        }
        userStrokeAppointmentOrderService.save(userStrokeAppointmentOrder);

        Map<String, String> param = new HashMap<>(16);
        param.put("sid", sid);
        param.put(ORDER_ID, orderId);

        PayParamDTO payParamDTO = createOrderPay(parkId, orderId, carParkAppointmentSet.getAmount(), DisplayUtil.ORDER_TYPE_0);

        Map<String, Object> map = new HashMap<>(16);
        map.put("strokeInfo", param);
        map.put("payParam", payParamDTO);

        userStrokeAppointmentOrder.freeData();
        carParkAppointmentSet.freeData();
        dto.freeData();
        return ApiResult.success(map);
    }

    /**
     * 取消预约
     *
     * @param sid           行程ID
     * @param orderId       订单ID
     * @param processStatus 过程状态
     * @param fullRefund    是否全额退款，如果是保安取消的，全部退，无需验证预约超时时间
     * @param note          取消原因
     * @return 成功or失败
     */
    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> cancelReserve(String sid, String orderId, Integer processStatus, boolean fullRefund, String note) {
        if (log.isDebugEnabled()) {
            log.debug("取消预约,sid: {},orderId: {},processStatus: {},fullRefund: {},note: {}", sid, orderId, processStatus, fullRefund, note);
        }
        LocalDateTime now = LocalDateTime.now();
        UserStrokeDTO stroke = userStrokeService.getByIdDto(sid);
        if (null == stroke) {
            log.info("行程不存在,sid:{}", sid);
            throw new ApiException(" 行程不存在");
        }
        Integer oldProcess = stroke.getProcessStatus();
        if (!oldProcess.equals(DisplayUtil.PROCESS_STATUS_0) && !oldProcess.equals(DisplayUtil.PROCESS_STATUS_1) && !oldProcess.equals(DisplayUtil.PROCESS_STATUS_2)) {
            log.info("行程不能取消,processStatus:{}", oldProcess);
            throw new ApiException("行程不能取消");
        }

        //完成退款动作,如果在预约超时时间内全部退，超过时间以后退部分
        //更新订单中实际支付金额
        if (fullRefund) {
            //不处理，直接全额退款
        }
        stroke.setProcessStatus(processStatus);
        if (!StringUtils.hasLength(orderId)) {
            final UserStrokeOrder lastReserve = userStrokeOrderService.getLastReserve(sid);
            orderId = null == lastReserve ? null : lastReserve.getOrderId();
            if (log.isDebugEnabled()) {
                log.debug("预约订单退款,orderId为空，根据sid查询预约订单，sid: {},是否存在已支付预约订单: {}", sid, orderId);
            }
        }
        if (StringUtils.hasLength(orderId)) {
            //说明当前预约订单是支付了钱的（即便费用为0）,判断订单是否存在，如果不存在，可能是支付成功的状态没有及时返回
            //订单没有及时创建
            UserStrokeOrder userStrokeOrder = userStrokeOrderService.getById(orderId);
            if (null == userStrokeOrder) {
                log.info("订单不存在,orderId:{}", orderId);
                throw new ApiException("订单存在");
            }

            //
            if (userStrokeOrderRefundService.isExistProcess(stroke.getSid(), userStrokeOrder.getBid(), stroke.getAmount())) {
                log.info("正在退款中，请勿重复点击,sid: {},bid: {},amount: {}", stroke.getSid(), userStrokeOrder.getBid(), stroke.getAmount());
                throw new ApiException("正在退款中，请勿重复点击");
            }

            if (Objects.equals(userStrokeOrder.getPayStatus(), DisplayUtil.PAY_STATUS_1)) {
                log.info("执行退款");
                //支付成功的
                //可预约车位+1
                parkCacheService.incr(stroke.getParkId());
                //前往停车场停车的数量-1
                parkCacheService.goToDecr(stroke.getParkId());
                log.info("执行退款，payAmount: {},signum：{}", userStrokeOrder.getPayAmount(), userStrokeOrder.getPayAmount().signum());
                if (userStrokeOrder.getPayAmount().signum() == BigInteger.ONE.signum()) {
                    //金额大于0的，需要退款
                    refund(sid, stroke.getAmount(), now, "预约超时退款");
                }
            }
        } else {
            //没有订单的，说明是弱预约，只需要把前往停车场停车的数量-1
            parkCacheService.goToDecr(stroke.getParkId());
        }
        //结束行程
        saveHistory(processStatus, userStrokeService.dtoToModel(stroke), note);
        stroke.freeData();
        return ApiResult.success("取消成功");
    }

    /**
     * 人工值守的停车场，入场和出场都需要保安确认
     *
     * @param sid         行程ID
     * @param parkId      停车场ID
     * @param direction   方向
     * @param plateNumber 车牌号
     */
    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> scanCode(String sid, String parkId, Integer direction, String plateNumber) {
        LocalDateTime now = LocalDateTime.now();
        checkPlateNumber(plateNumber);
        final CarPark carPark = Optional.ofNullable(carParkService.getById(parkId)).orElseThrow(() -> new ApiException(NULL_PARK_ID_MSG));
        if (carPark.getEnable().equals(ConstantDto.SF0)) {
            throw new ApiException("停车场未启用");
        }
        UserStrokeDTO userStrokeDTO = Optional.ofNullable(userStrokeService.getByIdDto(sid)).orElseThrow(() -> new ApiException(NULL_SID_MSG));
        if (direction.equals(PandaConstant.DIRECTION_1)) {

            if (!userStrokeDTO.getParkId().equals(parkId)) {
                throw new ApiException("预约的停车场不是当前扫码的停车场");
            }
            if (!userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_0)) {
                log.error("当前行程状态不支持扫码进入,sid: {},processStatus: {}", userStrokeDTO.getSid(), userStrokeDTO.getProcessStatus());
                throw new ApiException("当前行程状态不支持扫码进入");
            }
            if (carPark.getConfirmIn().equals(ConstantDto.SF1)) {
                //入场需要确认
                UserStrokeConfirmDTO query = new UserStrokeConfirmDTO();
                query.setParkId(parkId);
                query.setUserId(SecurityUtils.getLoginUser().getUserId());
                query.setPlateNumber(plateNumber);
                query.setDirection(direction);
                query.setConfirm(ConstantDto.SF0);
                query.setScanTime(now);
                final boolean b = userStrokeConfirmService.isExistsInCurrentDay(query);
                query.freeData();
                if (b) {
                    return ApiResult.error(1004, "已经有还没有处理的入场记录");
                }
            }
            if (carPark.getConfirmIn().equals(ConstantDto.SF1)) {
                //入场需要确认
                //修改状态为已扫码未入场
                userStrokeDTO.setProcessStatus(DisplayUtil.PROCESS_STATUS_7);
            } else {
                //修改状态为已入场
                userStrokeDTO.setProcessStatus(DisplayUtil.PROCESS_STATUS_3);
                userStrokeDTO.setIntoTime(now);
                userStrokeDTO.setBillStartTime(now);
            }
            userStrokeService.update(userStrokeDTO);
        }
//        else {
        //目前修改成出场不需要扫码确认，在支付完成以后自动放入确认中
//            if (carPark.getConfirmIn().equals(ConstantDto.SF0)) {
//                //不需要确认的，直接出场
//                userStrokeDTO.setOutTime(now);
//                saveHistory(DisplayUtil.PROCESS_STATUS_5, userStrokeService.dtoToModel(userStrokeDTO), "无需保安确认，自动离场");
//            }
//        }
        //判断是否需要保安确认
        if (carPark.getConfirmIn().equals(ConstantDto.SF1)) {
            userStrokeConfirmService.saveOrUpdate(sid, direction, plateNumber, now, carPark, SecurityUtils.getLoginUser().getUserId());
        }
        carPark.freeData();
        userStrokeDTO.freeData();
        return ApiResult.success();
    }

    /**
     * 付款以后
     *
     * @param bid       业务ID
     * @param payStatus 支付状态
     * @param orderType 订单类型
     */
    @Transactional(rollbackFor = Exception.class)
    public void afterPay(String bid, Integer payStatus, Integer orderType) {
        switch (orderType) {
            case DisplayUtil.ORDER_TYPE_0:
                buildUserStrokeAppointmentOrder(bid, payStatus);
                break;
            case DisplayUtil.ORDER_TYPE_1:
            case DisplayUtil.ORDER_TYPE_2:
            case DisplayUtil.ORDER_TYPE_3:
                buildCarPark(bid, payStatus);
                break;
            case DisplayUtil.ORDER_TYPE_4:
                buildGas(bid, payStatus);
                break;
            case DisplayUtil.ORDER_TYPE_5:
//                buildShop(bid, payStatus);
                mallService.buildCommodity(bid, payStatus);
                break;
            case DisplayUtil.ORDER_TYPE_6:
                parkSpaceSyncService.parkSpaceTenant(bid, payStatus);
                break;
            case DisplayUtil.ORDER_TYPE_7:
                hkService.updateOrderStatus(bid, payStatus);
                break;
            case DisplayUtil.ORDER_TYPE_8:
                walletHolderService.updateOrderStatus(bid, payStatus);
                break;
            default:
                break;
        }
    }

    private void buildShop(String bid, Integer payStatus) {
        List<ProductOrderDTO> list = productOrderService.getListByBid(bid);
        if (null == list || list.isEmpty()) {
            log.info("订单不存在，bid:{}", list);
            return;
        }
        buildShopOrder(list, payStatus);
        if (payStatus.equals(DisplayUtil.PAY_STATUS_1)) {
            //通知第三方
            log.info("商城支付成功,通知第三方,订单ID： {}", bid);
            fruitService.sync(list);
        }
    }

    /**
     * 处理加油订单
     *
     * @param bid       业务ID
     * @param payStatus 支付状态
     */
    private void buildGas(String bid, Integer payStatus) {
        List<UserStrokeOrderDTO> list = userStrokeOrderService.getListByBid(bid);
        if (null == list || list.isEmpty()) {
            log.info("订单不存在，bid:{}", list);
            return;
        }
        buildOrder(list, payStatus);
        if (payStatus.equals(DisplayUtil.PAY_STATUS_1)) {
            //支付成功,生成积分
            buildUserIntegral(list);
            //通知第三方
            log.info("加油支付成功,通知第三方,订单ID： {}", bid);
            SpringContextHolder.publishEvent(new GasEvent(bid));
//            SpringContextHolder.publishEvent(new AutoHomeEvent(bid));
        } else {
            //删除已使用的积分，把积分退回给用户
            //如果使用了优惠款，也退还给用户
            clearUserIntegral(list);
            log.info("加油支付失败,订单ID： {},payStatus:{}", bid, payStatus);
        }

    }


    /**
     * 生成用户积分
     * 这里需要注意的是，停车订单是在离场以后生成，不是支付成功生成
     *
     * @param list 用户订单
     */
    public void buildUserIntegral(List<UserStrokeOrderDTO> list) {
        for (UserStrokeOrderDTO userStrokeOrderDTO : list) {
            String cardId = stringRedisTemplate.opsForValue().get(RedisTemplateUtil.getRedisCacheKey(GasOrderService.ORDER_ID_TO_CARD_ID, userStrokeOrderDTO.getOrderId()));
            if (StringUtils.hasLength(cardId)) {
                //使用了优惠劵，如果优惠劵指定了获取熊猫都得金额，那么就与熊猫豆，如果为空就没有熊猫豆
                Optional.ofNullable(userCardService.getByIdDto(cardId)).ifPresent(userCardDTO -> {
                    Integer needIntegral = userCardDTO.getCardInfoDTO().getNeedIntegral();
                    if (null != needIntegral && needIntegral > 0) {
                        String note = "";
                        if (userStrokeOrderDTO.getTripType().equals(DisplayUtil.TRIP_TYPE_2)) {
                            note = "加油";
                        }
                        note += "使用优惠劵,获得熊猫豆: " + needIntegral + "个";
                        asyncService.createIntegral(userStrokeOrderDTO.getUserId(), BigDecimal.valueOf(needIntegral), note, PandaConstant.POINT_TYPE_GAS, userStrokeOrderDTO.getOrderId());
                    }
                });
            } else {
                String note = "";
                if (userStrokeOrderDTO.getTripType().equals(DisplayUtil.TRIP_TYPE_2)) {
                    note = "加油";
                }
                Money integral = new Money(userStrokeOrderDTO.getPayAmount());
                note += "金额: " + userStrokeOrderDTO.getPayAmountDisplay() + "元,获得熊猫豆: " + integral + "个";
                asyncService.createIntegral(userStrokeOrderDTO.getUserId(), integral.getAmount(), note, PandaConstant.POINT_TYPE_GAS, userStrokeOrderDTO.getOrderId());
            }

        }
    }

    /**
     * 删除用户积分和优惠劵
     * 这里需要注意的是，停车订单是在离场以后生成，不是支付成功生成
     *
     * @param list 用户订单
     */
    public void clearUserIntegral(List<UserStrokeOrderDTO> list) {
        for (UserStrokeOrderDTO userStrokeOrderDTO : list) {
            asyncService.deleteIntegral(userStrokeOrderDTO.getOrderId());
            asyncService.updateCard(userStrokeOrderDTO.getOrderId());
        }
    }


    /**
     * 处理停车和充电订单
     *
     * @param bid       业务ID
     * @param payStatus 支付状态
     */
    private void buildCarPark(String bid, Integer payStatus) {
        List<UserStrokeOrderDTO> list = userStrokeOrderService.getListByBid(bid);
        if (null == list || list.isEmpty()) {
            log.info("订单不存在，bid:{}", list);
            return;
        }
        String sid = list.get(0).getSid();
        buildOrder(list, payStatus);
        final UserStrokeDTO userStrokeDTO = userStrokeService.getByIdDto(sid);
        if (null == userStrokeDTO) {
            log.info("行程不存在不存在，sid:{}", sid);
            return;
        }
        userStrokeDTO.setProcessStatus(DisplayUtil.PROCESS_STATUS_4);
        calculatedAmount(userStrokeDTO);
        userStrokeService.update(userStrokeDTO);
        {
            final CarPark carPark = carParkService.getById(userStrokeDTO.getParkId());
            if (carPark.getOwnBusiness().equals(ConstantDto.SF1)) {
                //是自营的，说明没有闸机，必须要保安确认才能离场
                userStrokeConfirmService.saveOrUpdate(sid, PandaConstant.DIRECTION_2, userStrokeDTO.getPlateNumber(), LocalDateTime.now(), carPark, userStrokeDTO.getUserId());
            } else {
                //查询出停车的订单,值包含成功的和失败的
                final List<UserStrokeOrderDTO> l = list.stream()
                        .filter(userStrokeOrderDTO -> userStrokeOrderDTO.getTripType().equals(DisplayUtil.TRIP_TYPE_0))
                        .filter(userStrokeOrderDTO -> userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_1) || userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_2))
                        .collect(toList());
                if (l.isEmpty()) {
                    return;
                }
                final UserStrokeOrderDTO userStrokeOrderDTO = l.get(0);
                if (StringUtils.hasLength(userStrokeOrderDTO.getThirdPartyOrderId())) {
                    //有闸机的需要调用数据中台告知支付成功
                    log.info("支付成功，告知闸机,第三方停车ID: {}", userStrokeOrderDTO.getThirdPartyOrderId());
                    boolean b = payStatus == DisplayUtil.PAY_STATUS_1;
                    dcService.payResultCallBack(carPark.getPId(), userStrokeOrderDTO.getThirdPartyOrderId(), false, b);
                }
            }

        }
        userStrokeDTO.freeData();
    }

    private void buildOrder(List<UserStrokeOrderDTO> list, Integer payStatus) {
        for (UserStrokeOrderDTO strokeOrder : list) {
            RLock lock = redissonClient.getLock(strokeOrder.getOrderId());
            try {
                try {
                    boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                    if (res) {
                        UserStrokeOrderDTO userStrokeOrderDTO = userStrokeOrderService.getByIdDto(strokeOrder.getOrderId());
                        if (strokeOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_1) || strokeOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_2)) {
                            //表示已经处理过了，无需处理
                            continue;
                        }
                        userStrokeOrderDTO.setPayStatus(payStatus);
                        userStrokeOrderService.update(userStrokeOrderDTO);
                        userStrokeOrderDTO.freeData();
                    } else {
                        throw new ApiException(ApiResult.error());
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error(REDIS_LOCKED_ERR, e);
                    throw new ApiException(ApiResult.error());
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void buildShopOrder(List<ProductOrderDTO> list, Integer payStatus) {
        for (ProductOrderDTO productOrder : list) {
            RLock lock = redissonClient.getLock(productOrder.getOrderId());
            try {
                try {
                    boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                    if (res) {
                        ProductOrderDTO productOrderDTO = productOrderService.getByIdDto(productOrder.getOrderId());
                        if (productOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_1) || productOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_2)) {
                            //表示已经处理过了，无需处理
                            continue;
                        }
                        productOrderDTO.setPayStatus(payStatus);
                        productOrderService.update(productOrderDTO);
                        productOrderDTO.freeData();
                    } else {
                        throw new ApiException(ApiResult.error());
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error(REDIS_LOCKED_ERR, e);
                    throw new ApiException(ApiResult.error());
                }
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * 处理预约订单
     *
     * @param bid       订单ID(预约单BID就是订单ID)
     * @param payStatus 支付结果
     */
    private void buildUserStrokeAppointmentOrder(String bid, Integer payStatus) {
        final UserStrokeAppointmentOrder appointmentOrder = userStrokeAppointmentOrderService.getById(bid);
        if (null == appointmentOrder) {
            return;
        }
        //预约支付，支付成功创建订单和行程
        //支付失败不处理
        //处理完成以后删除预约订单表数据
        if (payStatus.equals(DisplayUtil.PAY_STATUS_1)) {
            createAppointmentStroke(appointmentOrder);
        }
        userStrokeAppointmentOrderService.removeById(appointmentOrder.getOrderId());
        appointmentOrder.freeData();
    }

    /**
     * 退款以后
     *
     * @param sid          行程ID
     * @param refundId     退款单ID
     * @param refundStatus 退款状态
     */
    @Transactional(rollbackFor = Exception.class)
    public void afterRefund(String sid, String refundId, Integer refundStatus) {
        final UserStrokeOrderRefundDTO refundDTO = userStrokeOrderRefundService.getByIdDto(refundId);
        if (null == refundDTO) {
            log.error("退款单为空,不处理,refundId: {}", refundId);
            return;
        }
        if (!refundDTO.getRefundStatus().equals(DisplayUtil.REFUND_STATUS_0)) {
            log.error("已经处理过了,不处理,refundId: {}", refundDTO.getRefundId());
            //说明已经处理过了，不处理
            return;
        }
        final UserStrokeDTO userStroke = userStrokeService.getByIdDto(sid);
        if (null != userStroke) {
            calculatedAmount(userStroke);
            userStrokeService.update(userStroke);
            userStroke.freeData();
        }
        UserStrokeOrder userStrokeOrder = userStrokeOrderService.getById(refundDTO.getOrderId());
        log.info("订单ID: {},userStrokeOrder: {}", refundDTO.getOrderId(), userStrokeOrder);
        if (null != userStrokeOrder) {
            //短期租赁
            if (userStrokeOrder.getOrderType().equals(DisplayUtil.ORDER_TYPE_6)) {
                parkSpaceSyncService.updateTenantOrderState(refundDTO.getBid(), refundStatus);
            }
        }
        if (null == refundDTO.getRefundTime()) {
            refundDTO.setRefundTime(LocalDateTime.now());
        }

//        if (refundStatus.equals(DisplayUtil.REFUND_STATUS_1)) {
//            //退款成功的加油订单，如果使用了积分抵扣，需要退还给用户
//            if (userStroke.getTripType().equals(DisplayUtil.TRIP_TYPE_2)) {
//                asyncService.deleteIntegral(refundDTO.getOrderId());
//            }
//        }
        refundDTO.setRefundStatus(refundStatus);
        userStrokeOrderRefundService.update(refundDTO);
        refundDTO.freeData();
    }

    /**
     * 计算金额，支付成功以后或者退款成功以后
     *
     * @param userStrokeDTO 行程单
     */
    private void calculatedAmount(UserStrokeDTO userStrokeDTO) {
        ArrayNode nodes = objectMapper.createArrayNode();
        //支付完成通知，计算行程总金额
        Money payAmount = new Money();
        Money onlineAmount = new Money();
        Money orderAmount = new Money();
        final List<UserStrokeOrder> list = userStrokeOrderService.getOkList(userStrokeDTO.getSid(), true);
        if (list.isEmpty()) {
            userStrokeDTO.setPayAmount(new BigDecimal("0"));
            userStrokeDTO.setAmount(userStrokeDTO.getPayAmount());
            userStrokeDTO.setOnLinePayAmount(userStrokeDTO.getPayAmount());
        } else {
            //当前list按正序排列，最后一笔订单的结束时间作为行程中付款的结束时间
            int size = list.size();
            int last = list.size() - 1;
            for (int i = 0; i < size; i++) {
                UserStrokeOrder strokeOrder = list.get(i);
                final Money payAmountBigDecimal = new Money(strokeOrder.getPayAmount());
                payAmount = payAmount.add(new Money(strokeOrder.getPayAmount()));
                onlineAmount = onlineAmount.add(new Money(strokeOrder.getOnLinePayAmount()));
                orderAmount = orderAmount.add(new Money(strokeOrder.getOrderAmount()));
                final List<UserStrokeOrderRefund> rList = userStrokeOrderRefundService.getList(strokeOrder.getOrderId());
                ObjectNode objectNode = objectMapper.createObjectNode();
                objectNode.put(ORDER_ID, strokeOrder.getOrderId());
                objectNode.put("orderType", strokeOrder.getOrderType());
                objectNode.put(AMOUNT, BigDecimalUtil.buildDefaultScale(payAmountBigDecimal.getAmount()).stripTrailingZeros().toPlainString());
                if (!rList.isEmpty()) {
                    ArrayNode arrayNode = objectMapper.createArrayNode();
                    for (UserStrokeOrderRefund userStrokeOrderRefund : rList) {
                        ObjectNode errNode = objectMapper.createObjectNode();
                        final Money refundAmountBigDecimal = new Money(userStrokeOrderRefund.getRefundAmount());
                        errNode.put(ORDER_ID, userStrokeOrderRefund.getOrderId());
                        errNode.put("refundId", userStrokeOrderRefund.getRefundId());

                        errNode.put(AMOUNT, BigDecimalUtil.buildDefaultScale(refundAmountBigDecimal.getAmount()).stripTrailingZeros().toPlainString());
                        arrayNode.add(errNode);
                        payAmount = payAmount.subtract(refundAmountBigDecimal);
                        onlineAmount = onlineAmount.subtract(refundAmountBigDecimal);
                        //标识整个订单的金额，退了不减
//                    orderAmount = orderAmount.subtract(refundAmountBigDecimal);
                    }
                    objectNode.set("refundList", arrayNode);
                }
                nodes.add(objectNode);
                if (i == last) {
                    userStrokeDTO.setBillEndTime(Optional.ofNullable(userStrokeDTO.getBillEndTime()).orElse(strokeOrder.getEndTime()));
                }
            }
            userStrokeDTO.setPayAmount(BigDecimalUtil.buildDefaultScale(payAmount.getAmount()));
            userStrokeDTO.setOnLinePayAmount(BigDecimalUtil.buildDefaultScale(onlineAmount.getAmount()));
            userStrokeDTO.setAmount(BigDecimalUtil.buildDefaultScale(orderAmount.getAmount()));
            if (!nodes.isEmpty()) {
                boolean isUpdate = true;
                UserStrokeProcessDetail detail = userStrokeProcessDetailService.getById(userStrokeDTO.getSid());
                if (null == detail) {
                    detail = new UserStrokeProcessDetail();
                    isUpdate = false;
                }
                detail.setSid(userStrokeDTO.getSid());
                detail.setProcessDetails(nodes.toString());
                if (isUpdate) {
                    userStrokeProcessDetailService.updateById(detail);
                } else {
                    userStrokeProcessDetailService.save(detail);
                }
                detail.freeData();

            }
        }
    }

    /**
     * 放入延时队列，超时自动清理
     *
     * @param sid     行程ID
     * @param orderId 订单ID
     * @param parkId  停车场ID
     */
    public void addDelay(String sid, String orderId, String parkId) {
        CarParkAppointmentSet carParkAppointmentSet = carParkAppointmentSetService.getById(parkId);
        if (null == carParkAppointmentSet) {
            log.error("没有预约设置，停车场ID：{}", parkId);
            throw new ApiException("没有预约设置");
        }
        RBlockingQueue<UserStrokeDelayDTO> blockingQueue = redissonClient.getBlockingQueue(DELAY_QUEUE_NAME);
        RDelayedQueue<UserStrokeDelayDTO> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
        delayedQueue.offer(new UserStrokeDelayDTO().setSid(sid).setOrderId(orderId), carParkAppointmentSet.getTime(), TimeUnit.MINUTES);
//        DelayOrderQueueManager.getInstance().put(new UserStrokeDelay(sid, orderId), carParkAppointmentSet.getTime(), TimeUnit.MINUTES);
    }

    /**
     * 保安确认
     *
     * @param confirmDTO 参数
     * @return 成功 or 失败
     */
    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> confirm(ConfirmDTO confirmDTO) {
        LocalDateTime now = LocalDateTime.now();
        String cid = confirmDTO.getCid();

        UserStrokeConfirm userStrokeConfirm = Optional.ofNullable(userStrokeConfirmService.getById(cid)).orElseThrow(() -> new ApiException("当前行程确认不存在"));
        UserStrokeDTO userStrokeDTO = userStrokeService.getByIdDto(userStrokeConfirm.getSid());
        if (userStrokeConfirm.getConfirm().equals(ConstantDto.SF1)) {
            throw new ApiException("当前行程已经确认");
        }

        if (userStrokeConfirm.getDirection().equals(PandaConstant.DIRECTION_2)) {
            //出场的时候必须要有行程才行
            if (null == userStrokeDTO) {
                throw new ApiException("当前行程不存在");
            }
        }

        String notReason = confirmDTO.getNotReason();
        String note = confirmDTO.getNote();
        Integer direction = userStrokeConfirm.getDirection();
        log.info("notReason: {},note: {},direction: {}", notReason, note, direction);
        if (StringUtils.hasLength(notReason)) {
            note = notReason;
        }
        log.info("note: {},direction: {}", note, direction);
        if (direction == 1) {
            //进场
            confirmInto(confirmDTO, now, userStrokeConfirm, userStrokeDTO, note);
        } else {
            //出场
            confirmOut(now, userStrokeConfirm, userStrokeDTO, note);
        }

        userStrokeConfirm.setNote(note);
        userStrokeConfirm.setConfirm(ConstantDto.SF1);
        userStrokeConfirm.setConfirmTime(now);
        userStrokeConfirm.setPersonId(SecurityUtils.getLoginUser().getUserId());
        userStrokeConfirm.setDoorPostName(doorpostLoginService.getDoorpostMsg(userStrokeConfirm.getPersonId()));
        userStrokeConfirmService.update(userStrokeConfirmService.modelToDto(userStrokeConfirm));

        userStrokeConfirm.freeData();

        Optional.ofNullable(userStrokeDTO).ifPresent(UserStrokeDTO::freeData);
        userStrokeConfirm.freeData();
        confirmDTO.freeData();
        return ApiResult.success();
    }

    /**
     * 获得当前正在进行中的行程
     *
     * @param dto 当前用户
     * @return 行程信息
     */
    @Transactional(rollbackFor = Exception.class)
    public RunMyStrokeVO runMyStroke(UserStrokeDTO dto) {
        LocalDateTime now = LocalDateTime.now();

        final UserStroke userStroke = userStrokeService.getProcessStroke(dto);
        UserStrokeHistoryDTO lastDto;
        if (null == userStroke) {
            //没有正在进行中的行程，那么就拉取最后一次历史行程记录
            lastDto = userStrokeHistoryService.getLastDto(dto.getUserId(), null);
            if (null != lastDto) {
                final PersonCarPark personCarPark = parkStrokeHolderService.personCarPark(lastDto.getPlateNumber(), lastDto.getUserId(), lastDto.getParkId(), false, lastDto.getTripType());
                parkStrokeHolderService.buildHistoryStroke(personCarPark, lastDto);
//                log.info("PersonCarPark: {}", personCarPark);
                if (null != personCarPark) {
                    return BeanUtil.toBean(personCarPark, RunMyStrokeVO.class);
                }
            }
            throw new ApiException(ApiResultWrapper.NULL_ERROR, "没有行程信息");
        }
        final UserStrokeDTO userStrokeDTO = userStrokeService.modelToDto(userStroke);
        //创建用户行程信息
        UserStrokeVO userStrokeVO = createUserStrokeVO(userStrokeDTO);
        //创建用户停车信息
        UserParkStrokeVO parkStrokeVO = createUserParkStrokeVO(now, userStrokeDTO);
        final CarPark carPark = Optional.ofNullable(carParkService.getById(userStroke.getParkId())).orElseThrow(() -> new ApiException("停车场信息不存在"));
        List<UserStrokeOrderDTO> chargeOrderList = Collections.emptyList();
        if (carPark.getParkType().equals(PandaConstant.PARK_TYPE_CHARGING_STATION)) {
            //充电站，此时拉取充电站信息
            chargeOrderList = userStrokeOrderService.getChargeListBySid(userStrokeDTO.getSid());
        }
        //充电完成的结束时间
        LocalDateTime chargeEndTime = null;
        LocalDateTime chargeStartTime = null;
        if (!chargeOrderList.isEmpty()) {
            //清除没有确认的充电订单，条件是已经拔枪的情况
            clearCharge(chargeOrderList);
            if (!chargeOrderList.isEmpty()) {
                final UserStrokeOrderDTO order = chargeOrderList.get(0);
                chargeStartTime = order.getOrderCharge().getStartTime();
                if (order.getOrderCharge().getState().equals(DisplayUtil.CHARGE_STAT_4)) {
                    //已结束的，查询出结束的时间，作为计算停车费的开始时间，优惠30分钟计算停车费
                    chargeEndTime = order.getEndTime();
                }
                List<UserChargeStrokeVO> chargeList = getChargeList(now, chargeOrderList);
                userStrokeVO.setUserChargeStrokeList(chargeList);
            }
        }

        //已入场的，已缴费未出场的，这两种情况才需要计算费用
        if (userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_3) || userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_4)) {
            //是否计算停车费
            boolean calculateParkFee = true;
            Long freeTime = null;
            if (!chargeOrderList.isEmpty() && null == chargeEndTime) {
                //是充电订单，chargeEndTime为空的时候，说明没有结束充电，此时不计算停车费，属于免费
                calculateParkFee = false;
                parkStrokeVO = null;
            }
            if (calculateParkFee) {
                if (null == chargeStartTime && null == chargeEndTime) {
                    //表示停车的订单，不带充电的
                    freeTime = Duration.between(parkStrokeVO.getBillStartTime(), parkStrokeVO.getBillEndTime()).toMinutes();
                }
                if (null != chargeStartTime && null != chargeEndTime) {
                    parkStrokeVO.setChargeStartTime(chargeStartTime);
                    parkStrokeVO.setChargeEndTime(chargeEndTime);
                    freeTime = Duration.between(chargeStartTime, chargeEndTime.plusMinutes(30)).toMinutes();
                }
            }
            if (null != parkStrokeVO) {
                buildParkTime(carPark, userStrokeDTO, userStrokeVO, parkStrokeVO, freeTime);
            }
        }

        if (userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_4)) {
            lastDto = userStrokeHistoryService.getLastDto(dto.getUserId(), dto.getTripType());
            if (null != lastDto && null != parkStrokeVO) {
                parkStrokeVO.setBillStartTime(lastDto.getBillStartTime());
                parkStrokeVO.setBillEndTime(lastDto.getBillEndTime());
            }
        }
        //这种说明是预约订单
        if (userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_0) && null != parkStrokeVO) {
            parkStrokeVO.setBillStartTime(null);
            parkStrokeVO.setBillEndTime(null);
            final BigDecimal bigDecimal = BigDecimalUtil.buildDefaultScale(userStrokeDTO.getPayAmount());
            parkStrokeVO.setAmount(bigDecimal);
            parkStrokeVO.setAmountDisplay(bigDecimal.stripTrailingZeros().toPlainString());
        }
        if ((userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_1) || userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_2))) {
            if (null != parkStrokeVO) {
                //预约被取消了的，支付金额肯定是0，如果取消成功了的话
                parkStrokeVO.setAmount(new BigDecimal(0));
                parkStrokeVO.setAmountDisplay("0");
                parkStrokeVO.setBillStartTime(null);
                parkStrokeVO.setBillEndTime(null);
            }
        }
        if (null != parkStrokeVO) {
            //当前行程的预约单，如果有表示强预约
            UserParkStrokeVO finalParkStrokeVO = parkStrokeVO;
            Optional.ofNullable(userStrokeOrderService.getLastReserve(userStrokeDTO.getSid())).ifPresent(u -> finalParkStrokeVO.setStrongReservation(PandaConstant.STRONG_RESERVATION_1));
            if (null == parkStrokeVO.getStrongReservation()) {
                //预约了，但是没有支付订单，那么就是弱预约
                parkStrokeVO.setStrongReservation(PandaConstant.STRONG_RESERVATION_2);
            }
//            finalParkStrokeVO.freeData();
        }

        userStroke.freeData();
        userStrokeDTO.freeData();
        final PersonCarPark personCarPark = parkStrokeHolderService.personCarPark(userStrokeVO.getPlateNumber(), userStrokeVO.getUserId(), userStrokeVO.getParkId(), false, dto.getTripType());
        if (null != personCarPark) {
            RunMyStrokeVO myStrokeVO = BeanUtil.toBean(personCarPark, RunMyStrokeVO.class);
            userStrokeVO.setUserParkStroke(parkStrokeVO);
            myStrokeVO.setUserStrokeVO(userStrokeVO);
            return myStrokeVO;
        }
        throw new ApiException(ApiResultWrapper.NULL_ERROR, "没有行程信息");
    }

    private void clearCharge(List<UserStrokeOrderDTO> chargeOrderList) {
        final Iterator<UserStrokeOrderDTO> iterator = chargeOrderList.iterator();
        while (iterator.hasNext()) {
            final UserStrokeOrderDTO userStrokeOrderDTO = iterator.next();
            //表示还没有经过用户确认，此时没有开始充电
            if (userStrokeOrderDTO.getOrderCharge().getState() == DisplayUtil.CHARGE_STAT_0) {
                //需要判断用户时候把枪插在车上的，如果没有，需要删除
                final ChargeStatusDTO chargeStatusDTO = dcService.status(userStrokeOrderDTO.getPlateNumber());
                if (null != chargeStatusDTO && chargeStatusDTO.getStatus().equals(1)) {
                    userStrokeOrderService.removeById(userStrokeOrderDTO.getOrderId());
                    userStrokeOrderChargeService.removeById(userStrokeOrderDTO.getOrderId());
                    iterator.remove();
                    chargeStatusDTO.freeData();
                }
            }
        }
    }

    private List<UserChargeStrokeVO> getChargeList(LocalDateTime now, List<UserStrokeOrderDTO> chargeOrderList) {
        return chargeOrderList.stream().map(userStrokeOrderDTO -> {
            UserChargeStrokeVO chargeStrokeVO = new UserChargeStrokeVO();
            chargeStrokeVO.setEtcPay(userStrokeOrderDTO.getEtcPay());
            chargeStrokeVO.setEtcPayDisplay(userStrokeOrderDTO.getEtcPayDisplay());
            chargeStrokeVO.setPayStatus(userStrokeOrderDTO.getPayStatus());
            chargeStrokeVO.setPayStatusDisplay(userStrokeOrderDTO.getPayStatusDisplay());
            if (null == userStrokeOrderDTO.getStartTime()) {
                chargeStrokeVO.setStartTime(now);
            } else {
                chargeStrokeVO.setStartTime(userStrokeOrderDTO.getStartTime());
            }
            if (userStrokeOrderDTO.getOrderCharge().getState().equals(DisplayUtil.CHARGE_STAT_0)) {
                //还没有确认充电的时候没有开始时间
                chargeStrokeVO.setStartTime(null);
            }
            chargeStrokeVO.setEndTime(userStrokeOrderDTO.getEndTime());
            if (null != userStrokeOrderDTO.getOrderCharge().getChargeTime()) {
                chargeStrokeVO.setChargeTime(userStrokeOrderDTO.getOrderCharge().getChargeTime());
            }
            chargeStrokeVO.setTotalAmount(userStrokeOrderDTO.getPayAmount());
            chargeStrokeVO.setTotalAmountDisplay(userStrokeOrderDTO.getPayAmountDisplay());

            final UserStrokeOrderChargeDTO orderCharge = userStrokeOrderDTO.getOrderCharge();

            chargeStrokeVO.setOrderId(orderCharge.getOrderId());
            chargeStrokeVO.setChargeCapacity(orderCharge.getChargeCapacity());
            chargeStrokeVO.setChargeCapacityDisplay(orderCharge.getChargeCapacityDisplay());
            chargeStrokeVO.setState(orderCharge.getState());
            chargeStrokeVO.setSoc(orderCharge.getSoc());
            chargeStrokeVO.setStateDisplay(orderCharge.getStateDisplay());
            chargeStrokeVO.setElectricityFee(orderCharge.getElectricityFee());
            chargeStrokeVO.setElectricityFeeDisplay(orderCharge.getElectricityFeeDisplay());
            chargeStrokeVO.setServiceCharge(orderCharge.getServiceCharge());
            chargeStrokeVO.setServiceChargeDisplay(orderCharge.getServiceChargeDisplay());
            chargeStrokeVO.setTotalAmount(orderCharge.getTotalAmount());
            chargeStrokeVO.setTotalAmountDisplay(orderCharge.getTotalAmountDisplay());

            return chargeStrokeVO;
        }).collect(toList());
    }

    private UserParkStrokeVO createUserParkStrokeVO(LocalDateTime now, UserStrokeDTO userStrokeDTO) {
        //停车
        UserParkStrokeVO parkStrokeVO = new UserParkStrokeVO();
        parkStrokeVO.setBillStartTime(userStrokeDTO.getBillStartTime());
        parkStrokeVO.setBillEndTime(now);
        parkStrokeVO.setIntoTime(userStrokeDTO.getIntoTime());
        parkStrokeVO.setAppointmentTime(userStrokeDTO.getAppointmentTime());
        return parkStrokeVO;
    }


    private UserStrokeVO createUserStrokeVO(UserStrokeDTO userStrokeDTO) {
        //只是计算金额，不生成订单
        UserStrokeVO userStrokeVO = new UserStrokeVO();
        //充电
        userStrokeVO.setTripType(userStrokeDTO.getTripType());
        userStrokeVO.setParkId(userStrokeDTO.getParkId());
        userStrokeVO.setParkIdDisplay(userStrokeDTO.getParkIdDisplay());
        userStrokeVO.setPlateNumber(userStrokeDTO.getPlateNumber());
        userStrokeVO.setSid(userStrokeDTO.getSid());
        userStrokeVO.setProcessStatus(userStrokeDTO.getProcessStatus());
        userStrokeVO.setProcessStatusDisplay(userStrokeDTO.getProcessStatusDisplay());
        userStrokeVO.setUserId(userStrokeDTO.getUserId());
        return userStrokeVO;
    }

    private void buildParkTime(CarPark carPark, UserStrokeDTO userStrokeDTO, UserStrokeVO userStrokeVO, UserParkStrokeVO parkStrokeVO, Long freeTime) {
        final String thirdPartyBill = Optional.ofNullable(carPark.getThirdPartyBill()).orElse(ConstantDto.SF0);
        //是否第三方计费，如果是的话表示调用闸机的计费接口，否就是根据我们自己的规则计费
        if (thirdPartyBill.equals(ConstantDto.SF1)) {
            //最后一笔订单
            final UserStrokeOrder lastOrder = userStrokeOrderService.getLast(userStrokeDTO.getSid());
            if (null == lastOrder || !lastOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_1)) {
                //没有订单,或者有订单但是没有支付成功的,说明是第一次计算停车费用，chargeEndTime为空，说明是普通停车，第一次计算停车费传入停车场的免费时长
                //如果chargeEndTime不为空，说明是充电带停车，此时免费时长为chargeEndTime+30分钟
                //调用第三方获取支付费用，自己不计算
                if (null == freeTime) {
                    freeTime = Long.valueOf(carPark.getIntoFreeTime());
                    log.info("充电免费时长未指定,使用停车场配置的免费时长: {}", freeTime);
                } else {
                    log.info("使用指定免费时长: {}", freeTime);
                }
                PayInfoDTO pay = dcService.queryPay(userStrokeVO.getParkId(), userStrokeVO.getPlateNumber(), freeTime);
                if (null != pay) {
                    log.info("查询停车费用，返回值:{}", pay);
                    buildParkStrokeVo(parkStrokeVO, pay);
                } else {
                    log.info("查询停车费用为空，没有产生费用");
                }
            } else {
                final BigDecimal bigDecimal = BigDecimalUtil.buildDefaultScale(userStrokeOrderService.getBaseMapper().sumOrders(userStrokeDTO.getSid()));
                parkStrokeVO.setAmount(bigDecimal);
                //说明不是第一次支付停车费，此时没有免费时长
                PayInfoDTO pay = dcService.queryPay(userStrokeVO.getParkId(), userStrokeVO.getPlateNumber(), 0L);
                if (null != pay) {
                    if (null != pay.getTotalFee()) {
                        parkStrokeVO.setNewAmount(BigDecimalUtil.buildDefaultScale(new BigDecimal(pay.getTotalFee())));
                        parkStrokeVO.setNewAmountDisplay(parkStrokeVO.getNewAmount().stripTrailingZeros().toPlainString());
                    }
                    if (StringUtils.hasLength(pay.getOutTime())) {
                        LocalDateTime start = lastOrder.getEndTime();
                        if (null == start) {
                            //说明是预约订单，没有进入停车场，所以结束时间为空，此处取行程计费的开始时间
                            start = userStrokeDTO.getBillStartTime();
                        }
                        LocalDateTime endTime = DateUtil.strToLocalDateTime(pay.getOutTime());
                        final Duration duration = Duration.between(start, endTime);
                        parkStrokeVO.setParkTime(duration.toSeconds());
                        parkStrokeVO.setBillEndTime(endTime);
                    }
                }
            }
        } else {
            //自己计算数据
            if (null != parkStrokeVO.getBillStartTime() && null != parkStrokeVO.getBillEndTime()) {
                final Duration duration = Duration.between(parkStrokeVO.getBillStartTime(), parkStrokeVO.getBillEndTime());
                parkStrokeVO.setParkTime(duration.toSeconds());
            }
            //最后一笔订单
            final UserStrokeOrder lastOrder = userStrokeOrderService.getLast(userStrokeDTO.getSid());
            if (null == lastOrder || !lastOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_1)) {
                //没有订单,或者有订单但是没有支付成功的
                BigDecimal amount = BigDecimalUtil.buildDefaultScale(calculateAmount(userStrokeDTO.getSid(), null, userStrokeDTO.getParkId(), parkStrokeVO.getBillStartTime(), parkStrokeVO.getBillEndTime()));
                parkStrokeVO.setAmount(amount);
            } else {
                final BigDecimal bigDecimal = BigDecimalUtil.buildDefaultScale(userStrokeOrderService.getBaseMapper().sumOrders(userStrokeDTO.getSid()));
                parkStrokeVO.setAmount(bigDecimal);
                LocalDateTime start = lastOrder.getEndTime();
                if (null == start) {
                    //说明是预约订单，没有进入停车场，所以结束时间为空，此处取行程计费的开始时间
                    start = userStrokeDTO.getBillStartTime();
                }
                //计算最后一笔订单的时间到现在的时间是否会产生新的费用
                BigDecimal amount = BigDecimalUtil.buildDefaultScale(calculateAmount(userStrokeDTO.getSid(), null, userStrokeDTO.getParkId(), start, parkStrokeVO.getBillEndTime()));
                if (amount.doubleValue() > 0) {
                    //新的需要支付的金额
                    parkStrokeVO.setNewAmount(BigDecimalUtil.buildDefaultScale(amount));
                    parkStrokeVO.setNewAmountDisplay(parkStrokeVO.getNewAmount().stripTrailingZeros().toPlainString());
                }
                lastOrder.freeData();
            }
        }
        if (null != parkStrokeVO) {
            if (null == parkStrokeVO.getParkTime()) {
                if (null != parkStrokeVO.getBillStartTime() && null != parkStrokeVO.getBillEndTime()) {
                    final Duration duration = Duration.between(parkStrokeVO.getBillStartTime(), parkStrokeVO.getBillEndTime());
                    parkStrokeVO.setParkTime(duration.toSeconds());
                }
            }
            Optional.ofNullable(parkStrokeVO.getParkTime()).ifPresent(parkTime -> parkStrokeVO.setParkTimeDisplay(DisplayUtil.displayParkTime(parkTime)));
            Optional.ofNullable(parkStrokeVO.getAmount()).ifPresent(amount -> parkStrokeVO.setAmountDisplay(amount.stripTrailingZeros().toPlainString()));
        }
        log.info("parkStrokeVO: {}", parkStrokeVO);
    }

    private void buildParkStrokeVo(UserParkStrokeVO parkStrokeVO, PayInfoDTO pay) {
        if (null != pay.getStayedTime()) {
            parkStrokeVO.setParkTime((long) (pay.getStayedTime() * 60));
        }
        if (StringUtils.hasLength(pay.getTotalFee())) {
            parkStrokeVO.setAmount(BigDecimalUtil.buildDefaultScale(new BigDecimal(pay.getTotalFee())));
        }
        if (StringUtils.hasLength(pay.getInTime())) {
            parkStrokeVO.setBillStartTime(DateUtil.strToLocalDateTime(pay.getInTime()));
        }
        if (StringUtils.hasLength(pay.getOutTime())) {
            parkStrokeVO.setBillEndTime(DateUtil.strToLocalDateTime(pay.getOutTime()));
        }
    }

    /**
     * 创建订单
     *
     * @param parkId    停车场ID
     * @param bid       业务ID
     * @param payAmount 金额
     * @param orderType 订单类型
     * @return 支付参数
     */
    public PayParamDTO createOrderPay(String parkId, String bid, BigDecimal payAmount, Integer orderType) {
        log.info("用户支付金额: {}", payAmount);
        if (SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_DEV)) {
            //测试环境，不调起支付
            return new PayParamDTO();
        }
        OrderDTO orderDto = new OrderDTO();
        orderDto.setThirdPartyOrder(bid).
                setPayAmount(payAmount)
                .setNotifyUrl(pandaParkConfig.getDomain() + "/gy-panda-park/app/userStroke/notify/payAfter")
                .setPersonId(SecurityUtils.getLoginUser().getUserId())
                .setType(PayConstant.ORDER_TYPE_2)
                .setAppId(SecurityUtils.getLoginUser().getUser().getAppUserExtend().getAppId())
                .setOpenid(SecurityUtils.getLoginUser().getUser().getAppUserExtend().getOpenid())
                .setPartner(pandaParkConfig.getDefaultPartner())
                .setSubPartner(getSubPartner(parkId));

        ObjectNode attachObjectNode = objectMapper.createObjectNode();
        if (Objects.equals(orderType, DisplayUtil.ORDER_TYPE_0)) {
            orderDto.setBody("停车场预约停车缴费").setSubject("预约停车");
            attachObjectNode.put("orderType", DisplayUtil.ORDER_TYPE_0);
        } else if (Objects.equals(orderType, DisplayUtil.ORDER_TYPE_1) || Objects.equals(orderType, DisplayUtil.ORDER_TYPE_2)) {
            orderDto.setBody("停车场停车缴费").setSubject("停车");
            attachObjectNode.put("orderType", DisplayUtil.ORDER_TYPE_1);
        } else if (Objects.equals(orderType, DisplayUtil.ORDER_TYPE_3)) {
            orderDto.setBody("停车场充电缴费").setSubject("充电");
            attachObjectNode.put("orderType", DisplayUtil.ORDER_TYPE_3);
        } else if (Objects.equals(orderType, DisplayUtil.ORDER_TYPE_4)) {
            orderDto.setBody("加油站加油缴费").setSubject("加油");
            attachObjectNode.put("orderType", DisplayUtil.ORDER_TYPE_4);
        } else if (Objects.equals(orderType, DisplayUtil.ORDER_TYPE_6)) {
            orderDto.setBody("停车场停车缴费").setSubject("短期车位租赁");
            attachObjectNode.put("orderType", DisplayUtil.ORDER_TYPE_6);
        } else {
            throw new ApiException("订单类型异常");
        }
        orderDto.setAttach(attachObjectNode);
        ObjectNode child = objectMapper.createObjectNode();
        ObjectNode wxLite = objectMapper.createObjectNode();
        wxLite.put(AMOUNT, payAmount.doubleValue());
        wxLite.put("sub_appid", SecurityUtils.getLoginUser().getUser().getAppUserExtend().getAppId());
        wxLite.put("sub_openid", SecurityUtils.getLoginUser().getUser().getAppUserExtend().getOpenid());
        child.set("wx_lite", objectMapper.createArrayNode().add(wxLite));
        orderDto.setChannelDetails(child);

        Map<String, String> params = orderHolderService.createOrder(orderDto);
        if (params.isEmpty()) {
            throw new ApiException("获取支付参数异常");
        }
        PayParamDTO payParamDTO = new PayParamDTO();
        payParamDTO.setAppId(pandaParkConfig.getDefaultMiniProgramAppId()).setNonceStr(params.get("nonceStr")).setPrepayId(params.get("package")).setPaySign(params.get("paySign")).setSignType("MD5").setTimeStamp(params.get("timeStamp")).setThirdPartyOrder(bid);
        return payParamDTO;
    }

    /**
     * 创建订单
     *
     * @param sid       行程ID
     * @param payAmount 需要支付的金额
     * @param type      0单笔 1全部
     * @param tripType  0停车 1充电
     * @param orderId   订单ID，只有充电订单有订单ID
     */
    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> createOrder(String sid, Object payAmount, int type, Object orderId, Integer tripType) {
        if (type == 0) {
            if (null == payAmount) {
                throw new ApiException("单笔支付金额不能为空");
            }
            if (null == tripType) {
                throw new ApiException("单笔支付行程类型不能为空");
            }
        }
        final UserStrokeDTO userStrokeDTO = userStrokeService.getByIdDto(sid);
        if (null == userStrokeDTO) {
            return ApiResult.error(NULL_SID_MSG);
        }
        final CarPark carPark = carParkService.getById(userStrokeDTO.getParkId());
        if (null == carPark) {
            return ApiResult.error(NULL_PARK_ID_MSG);
        }
        Integer orderType = null;
        List<UserStrokeOrderDTO> payList = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        String bid = IdWorker.getIdStr();
        if (type == 1 || tripType == DisplayUtil.TRIP_TYPE_0) {
            final UserStrokeOrderDTO parkFee = getParkFee(userStrokeDTO, carPark, bid, now, payAmount);
            if (null != parkFee) {
                orderType = parkFee.getOrderType();
                payList.add(parkFee);
            }
        }
        if (type == 1 || tripType == DisplayUtil.TRIP_TYPE_1) {
            orderType = DisplayUtil.ORDER_TYPE_3;
            buildChargeFee(sid, type, orderId, payList, bid);
        }
        if (payList.isEmpty()) {
            throw new ApiException("没有需要支付的订单");
        }
        Money money = new Money(0);
        for (UserStrokeOrderDTO userStrokeOrderDTO : payList) {
            money = money.add(new Money(userStrokeOrderDTO.getPayAmount()));
        }
        log.info("支付金额: {}", money);
        return ApiResult.success(createOrderPay(userStrokeDTO.getParkId(), bid, money.getAmount(), orderType));
    }

    /**
     * 处理充电费用
     *
     * @param sid     行程ID
     * @param type    0单笔 1全部
     * @param orderId 订单ID，只有充电订单有订单ID
     * @param payList 需要计算费用的订单
     * @param bid     业务ID
     */
    private void buildChargeFee(String sid, int type, Object orderId, List<UserStrokeOrderDTO> payList, String bid) {
        if (type == 0) {
            //单笔的时候，orderId不能为空
            if (null == orderId) {
                throw new ApiException("单笔支付充电费用的时候订单ID不能为空");
            }
            final UserStrokeOrderDTO userStrokeOrder = Optional.ofNullable(userStrokeOrderService.getByIdDto(orderId.toString())).orElseThrow(() -> new ApiException("订单ID不存在"));
            if (!userStrokeOrder.getOrderCharge().getState().equals(DisplayUtil.CHARGE_STAT_4)) {
                throw new ApiException("当前充电订单为结束，不能支付");
            }
            if (userStrokeOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_1)) {
                throw new ApiException("当前订单支付成功，请勿重复支付");
            }
            payList.add(userStrokeOrder);
        } else {
            final List<UserStrokeOrderDTO> chargeList = userStrokeOrderService.getChargeListBySid(sid);
            if (!chargeList.isEmpty()) {
                for (UserStrokeOrderDTO userStrokeOrderDTO : chargeList) {
                    if (userStrokeOrderDTO.getOrderCharge().getState().equals(DisplayUtil.CHARGE_STAT_4)) {
                        if (userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_0) || userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_2) || userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_4)) {
                            userStrokeOrderDTO.setBid(bid);
                            userStrokeOrderService.update(userStrokeOrderDTO);
                            payList.add(userStrokeOrderDTO);
                        }
                    }
                }
            }
        }
    }

    /**
     * 获得停车费并保存
     *
     * @param userStrokeDTO 行程
     * @param carPark       停车场
     * @param bid           业务ID
     * @param now           当前时间
     * @param payAmount     金额
     * @return 订单
     */
    private UserStrokeOrderDTO getParkFee(UserStrokeDTO userStrokeDTO, CarPark carPark, String bid, LocalDateTime now, Object payAmount) {
        //看是否是第三方计费，如果是不用计算费用
        final String thirdPartyBill = carPark.getThirdPartyBill();
        UserStrokeOrderDTO userStrokeOrderDTO;
        if (thirdPartyBill.equals(ConstantDto.SF1)) {
            userStrokeOrderDTO = buildThirdOrder(userStrokeDTO, carPark, bid, ConstantDto.SF0);
//            if (userStrokeOrderDTO.getPayAmount().longValue() < 1) {
//                //免费的情况，修改当前行程的状态为已缴费未出场
//                userStrokeDTO.setProcessStatus(DisplayUtil.PROCESS_STATUS_4);
//                userStrokeService.update(userStrokeDTO);
//            }
        } else {
            //获得已经缴费的最后一笔订单信息,如果有就作为开始时间计算金额，如果没有使用行程单中的支付开始时间
            LocalDateTime billStartTime = Optional.ofNullable(userStrokeOrderService.getLast(userStrokeDTO.getSid())).map(UserStrokeOrder::getEndTime).orElse(userStrokeDTO.getBillStartTime());
            userStrokeOrderDTO = createOrder(userStrokeDTO, billStartTime, now, bid);
            if (null == userStrokeOrderDTO) {
                userStrokeDTO.setBillEndTime(now);
                userStrokeDTO.setOutTime(now);
                if (carPark.getOwnBusiness().equals(ConstantDto.SF1)) {
                    //免费的情况，修改当前行程的状态为已缴费未出场
                    userStrokeDTO.setProcessStatus(DisplayUtil.PROCESS_STATUS_4);
                    userStrokeService.update(userStrokeDTO);
                    //是自营的，说明没有闸机，必须要保安确认才能离场
                    userStrokeConfirmService.saveOrUpdate(userStrokeDTO.getSid(), PandaConstant.DIRECTION_2, userStrokeDTO.getPlateNumber(), LocalDateTime.now(), carPark, userStrokeDTO.getUserId());
                    userStrokeDTO.freeData();
                }
            } else {
                if (null != payAmount) {
                    final BigDecimal bigDecimal = new BigDecimal(payAmount.toString());
                    if (userStrokeOrderDTO.getPayAmount().compareTo(bigDecimal) != BigInteger.ZERO.signum()) {
                        throw new ApiException("金额已经变化，请刷新后再试");
                    }
                }
            }
        }
        return userStrokeOrderDTO;
    }

    /**
     * 调用第三方接口生成停车订单
     *
     * @param userStrokeDTO 行程信息
     * @param carPark       停车场信息
     * @param bid           订单ID
     * @param etcPay        是否ETC支付
     * @return 订单信息
     */
    private UserStrokeOrderDTO buildThirdOrder(UserStrokeDTO userStrokeDTO, CarPark carPark, String bid, String etcPay) {
        long freeTime = userStrokeOrderService.parkPaySuccess(userStrokeDTO.getSid()) ? 0L : Long.parseLong(String.valueOf(carPark.getIntoFreeTime()));
        final PayInfoDTO payInfoDTO = dcService.queryPay(carPark.getPId(), userStrokeDTO.getPlateNumber(), freeTime);
        if (null == payInfoDTO) {
            throw new ApiException("查询停车费失败");
        }
        Money money = new Money(payInfoDTO.getLastFee());
        Integer orderType = freeTime == 0L ? DisplayUtil.ORDER_TYPE_1 : DisplayUtil.ORDER_TYPE_2;
        final LocalDateTime inTime = DateUtil.strToLocalDateTime(payInfoDTO.getInTime());
        final LocalDateTime outTime = DateUtil.strToLocalDateTime(payInfoDTO.getOutTime());

        UserStrokeOrderDTO query = new UserStrokeOrderDTO();
        query.setSid(userStrokeDTO.getSid());
        query.setUserId(userStrokeDTO.getUserId());
        query.setPayAmount(money.getAmount());
        query.setTripType(DisplayUtil.TRIP_TYPE_0);
        query.setStartTime(inTime);
        final UserStrokeOrderDTO userStrokeOrderDTO = userStrokeOrderService.getOneDto(query);
        query.freeData();
        if (null == userStrokeOrderDTO) {
            return createStrokeOrder(userStrokeDTO.getSid(), bid, carPark.getPId(), userStrokeDTO.getPlateNumber(),
                    money.getAmount(), orderType, DisplayUtil.PAY_STATUS_0, etcPay,
                    DisplayUtil.TRIP_TYPE_0, userStrokeDTO.getUserId(), null, inTime,
                    outTime, payInfoDTO.getPayNo(), null);
        } else {
            userStrokeOrderDTO.setBid(bid);
            userStrokeOrderDTO.setThirdPartyOrderId(payInfoDTO.getPayNo());
            try {
                if (userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_1) || userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_3)) {
                    //说明该笔停车费已经支付成功了
                    return null;
                } else {
                    userStrokeOrderDTO.setEndTime(outTime);
                    return userStrokeOrderDTO;
                }
            } finally {
                userStrokeOrderService.update(userStrokeOrderDTO);
            }
        }
    }

    /**
     * 创建订单
     *
     * @param userStroke 行程信息
     * @param start      开始时间
     * @param end        结束时间
     * @param bid        业务ID
     * @return 订单ID
     */
    private UserStrokeOrderDTO createOrder(UserStrokeDTO userStroke, LocalDateTime start, LocalDateTime end, String bid) {
        final CarPark carPark = carParkService.getById(userStroke.getParkId());
        /*
         * 判断时间是否超过了免费时长，超过了才计算费用，生成订单
         */
        if (!isTimeOut(start, end, carPark.getPayFreeTime())) {
            return null;
        }
        String orderId = IdWorker.getIdStr();
        //计算缴费金额
        BigDecimal bigDecimal = BigDecimalUtil.buildDefaultScale(calculateAmount(userStroke.getSid(), orderId, userStroke.getParkId(), start, end));
        log.info("计算缴费金额: {}", bigDecimal);
        userStroke.setPayAmount(bigDecimal);
        userStroke.setOnLinePayAmount(bigDecimal);
        userStroke.setAmount(bigDecimal);
        if (bigDecimal.doubleValue() > 0) {
            boolean b = userStrokeOrderService.isExistsByOrder(userStroke.getSid(), userStroke.getParkId(), start, bigDecimal);
            if (b) {
                throw new ApiException("已经存在，请勿重复提交");
            }
            int orderType;
            if (null != userStroke.getProcessStatus() && userStroke.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_4)) {
                //如果当前行程的状态是已缴费未出场，那么这个时候在缴费肯定就是补交费呢
                orderType = DisplayUtil.ORDER_TYPE_2;
            } else {
                orderType = DisplayUtil.ORDER_TYPE_1;
            }
            final String promotionCode = SecurityUtils.getLoginUser().getUser().getAppUserExtend().getPromotionCode();
            return createStrokeOrder(userStroke.getSid(), IdWorker.getIdStr(), userStroke.getParkId(), userStroke.getPlateNumber(), userStroke.getPayAmount(), orderType, DisplayUtil.PAY_STATUS_0, ConstantDto.SF0, DisplayUtil.TRIP_TYPE_0, userStroke.getUserId(), userStroke.getNickName(), start, end, null, promotionCode, bid);
        }
        return null;
    }

    /**
     * 计算停车费
     *
     * @param sid     行程id
     * @param orderId 订单ID
     * @param parkId  停车场ID
     * @param start   停车开始时间
     * @param end     停车结束时间
     * @return 金额
     */
    @SneakyThrows
    private BigDecimal calculateAmount(String sid, String orderId, String parkId, LocalDateTime start, LocalDateTime end) {
        CarParkFee carParkFee = carParkFeeService.getUniqueByPId(parkId);
        if (null == carParkFee) {
            throw new ApiException("没有设置停车场费用");
        }
        String billRule = carParkFee.getBillRule();
        String rule = carParkFee.getRule();
        if (!StringUtils.hasLength(billRule)) {
            throw new ApiException("停车场计费规则为空");
        }
        if (!StringUtils.hasLength(rule)) {
            throw new ApiException("规则为空");
        }
        if (null == start) {
            throw new ApiException("计费开始时间不能为空");
        }
        if (null == end) {
            throw new ApiException("计费结束时间不能为空");
        }
        final UserStroke userStroke = Optional.ofNullable(userStrokeService.getById(sid)).orElseThrow(() -> new ApiException(NULL_SID_MSG));
        final Duration duration = Duration.between(start, end);
        final CarPark carPark = carParkService.getById(parkId);
        if (userStroke.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_3)) {
            //说明还没有交过费，此时需要判断该车是否超过了免费入场时间
            if (duration.toMinutes() <= carPark.getIntoFreeTime()) {
                //还在免费时长，不需要缴费
                return new BigDecimal("0");
            }
        }
        if (userStroke.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_4)) {
            //已经交过一次费了，此时判断是否超过了出场免费时长
            if (duration.toMinutes() <= carPark.getPayFreeTime()) {
                //还在免费时长，不需要缴费
                return new BigDecimal("0");
            }
        }
        BaseRuleDTO baseRuleDTO = billRuleSetService.getRuleDTO(billRule, objectMapper.readTree(rule));

        PriceReqVO req = new PriceReqVO().setBillRule(billRule).setOrderNo(orderId).setRules(Collections.singletonList(baseRuleDTO)).setId(sid).setStartTime(start).setEndTime(end);
        LiteflowResponse response = flowExecutor.execute2Resp(billRule, req, PriceContext.class);
        if (null != response.getCause()) {
            throw new ApiException(response.getCause().getMessage());
        }
        List<String> printLog = response.getContextBean(PriceContext.class).getPrintLog();
        BigDecimal finalOrderPrice = response.getContextBean(PriceContext.class).getFinalOrderPrice();
        log.info("printLog:{}", printLog);
        return finalOrderPrice;
    }

    private CarParkAppointmentSet checkAppointment(ReserveDTO dto) {
        CarParkAppointmentSet carParkAppointmentSet = carParkAppointmentSetService.getById(dto.getParkId());
        if (null == carParkAppointmentSet) {
            throw new ApiException("当前停车场没有设置预约信息");
        }
        UserStrokeDTO query = new UserStrokeDTO();
        query.setUserId(SecurityUtils.getLoginUser().getUserId());
        query.setTripStatus(DisplayUtil.TRIP_STATUS_1);
        query.setPlateNumber(dto.getPlateNumber());
        if (Boolean.TRUE.equals(userStrokeService.isExists(query))) {
            throw new ApiException("有行程信息不能预约");
        }

        QueryWrapper<UserStrokeAppointmentOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", query.getUserId());
        queryWrapper.eq("plate_number", dto.getPlateNumber());
        queryWrapper.in("pay_status", DisplayUtil.PAY_STATUS_0);
        if (userStrokeAppointmentOrderService.isExists(queryWrapper)) {
            log.info("有行程信息不能预约,userId:{},plateNumber:{}", query.getUserId(), dto.getPlateNumber());
            throw new ApiException("有预约未支付信息，请尽快支付");
        }
        query.freeData();
        queryWrapper.clear();
        return carParkAppointmentSet;
    }

    private void checkCarPark(String parkId) {
        CarPark carPark = carParkService.getById(parkId);
        if (null == carPark) {
            throw new ApiException(NULL_PARK_ID_MSG);
        }
        if (carPark.getEnable().equals(ConstantDto.SF0)) {
            throw new ApiException("停车场未启用");
        }

    }

    private void saveHistory(Integer processStatus, UserStroke stroke, String note) {
        UserStrokeHistoryDTO history = new UserStrokeHistoryDTO();
        history.setSid(stroke.getSid());
        history.setNickName(stroke.getNickName());
        history.setStartTime(stroke.getStartTime());
        history.setAppointmentTime(stroke.getAppointmentTime());
        history.setIntoTime(stroke.getIntoTime());
        history.setOutTime(stroke.getOutTime());
        history.setBillStartTime(stroke.getBillStartTime());
        history.setBillEndTime(stroke.getBillEndTime());
        history.setParkTime(stroke.getParkTime());
        history.setAmount(stroke.getAmount());
        history.setPayAmount(stroke.getPayAmount());
        history.setOnLinePayAmount(stroke.getOnLinePayAmount());
        history.setProcessStatus(processStatus);
        if (StringUtils.hasLength(note)) {
            history.setNote(note);
        }
        if (null == stroke.getParkTime()) {
            if (null != stroke.getBillStartTime() && null != stroke.getBillEndTime()) {
                final Duration duration = Duration.between(stroke.getBillStartTime(), stroke.getBillEndTime());
                history.setParkTime(duration.toSeconds());
            }
        }
        history.setParkId(stroke.getParkId());
        history.setPlateNumber(stroke.getPlateNumber());
        history.setUserId(stroke.getUserId());
        history.setTripType(stroke.getTripType());
        final int strongReservation = Optional.ofNullable(userStrokeService.getBaseMapper().strongReservation(stroke.getSid(), stroke.getTripType())).orElse(0);
        history.setStrongReservation(strongReservation > 0 ? 1 : 2);
        userStrokeHistoryService.save(history);
        history.freeData();

        userStrokeService.removeById(stroke.getSid());
    }

    /**
     * 生成行程订单,弱预约或者强预约但是预约金额为0的
     *
     * @param dto 参数
     */
    private Map<String, String> createUserStroke(ReserveDTO dto) {
        final String userId = SecurityUtils.getLoginUser().getUserId();
        final String nickName = SecurityUtils.getLoginUser().getUser().getAppUserExtend().getNickName();
        final String promotionCode = SecurityUtils.getLoginUser().getUser().getAppUserExtend().getPromotionCode();
        Map<String, String> param = new HashMap<>();
        String sid = dto.getSid();
        String orderId = dto.getOrderId();
        if (!StringUtils.hasLength(sid)) {
            sid = IdWorker.getIdStr();
        }
        LocalDateTime now = LocalDateTime.now();
        //创建行程
        final UserStrokeDTO userStroke = createUserStroke(sid, dto.getParkId(), dto.getPlateNumber(), DisplayUtil.TRIP_TYPE_0, now, SecurityUtils.getLoginUser().getUserId(), new BigDecimal("0"));
        userStrokeService.save(userStroke);
        userStroke.freeData();

        //强预约，生成订单，保留车位，支付金额为0的也生成订单
        if (dto.getStrongReservation().equals(PandaConstant.STRONG_RESERVATION_1)) {
            if (!StringUtils.hasLength(orderId)) {
                orderId = IdWorker.getIdStr();
            }
            //强预约，支付金额为0，生成订单，支付状态为支付成功
            createStrokeOrder(sid, orderId, dto.getParkId(), dto.getPlateNumber(), new BigDecimal("0"), DisplayUtil.ORDER_TYPE_0, DisplayUtil.PAY_STATUS_1, ConstantDto.SF0, DisplayUtil.TRIP_TYPE_0, userId, nickName, now, now, null, promotionCode);
            //强预约，保留车位
            //可预约车位-1
            final int decr = parkCacheService.decr(dto.getParkId());
            log.info("支付金额为0的强预约，可预约车位数: {}", decr);
        }
        //放入延时队列，到期以后如果不停车自动取消
        addDelay(sid, orderId, dto.getParkId());
        //前往停车场停车的数量+1
        final int i = parkCacheService.goToIncr(dto.getParkId());
        log.info("前往停车场停车的数量: {}", i);
        {
            //判断是否需要订阅，第三方计费的停车场需要订阅
            subscribe(dto.getParkId(), dto.getPlateNumber());
        }
        param.put("sid", sid);
        param.put(ORDER_ID, orderId);
        return param;
    }

    /**
     * 预约支付成功以后生成行程订单
     *
     * @param appointmentOrder 用户预约订单
     */
    private void createAppointmentStroke(UserStrokeAppointmentOrder appointmentOrder) {
        final MiddleUser middleUser = middleUserService.getUniqueByUserIdAndAppId(appointmentOrder.getUserId(), pandaParkConfig.getDefaultMiniProgramAppId());
        String nickName = null;
        if (null != middleUser) {
            nickName = middleUser.getNickName();
        }

        final UserStrokeDTO userStroke = createUserStroke(appointmentOrder.getSid(), appointmentOrder.getParkId(), appointmentOrder.getPlateNumber(), appointmentOrder.getTripType(), appointmentOrder.getAppointmentTime(), appointmentOrder.getUserId(), appointmentOrder.getOrderAmount());
        userStrokeService.save(userStroke);
        userStroke.freeData();

        createStrokeOrder(appointmentOrder.getSid(), appointmentOrder.getOrderId(), appointmentOrder.getParkId(), appointmentOrder.getPlateNumber(), appointmentOrder.getOrderAmount(), DisplayUtil.ORDER_TYPE_0, DisplayUtil.PAY_STATUS_1, ConstantDto.SF0, appointmentOrder.getTripType(), appointmentOrder.getUserId(), nickName, appointmentOrder.getAppointmentTime(), appointmentOrder.getAppointmentTime(), null, appointmentOrder.getPromotionCode());

        //强预约，保留车位
        //可预约车位-1
        int decr = parkCacheService.decr(appointmentOrder.getParkId());
        log.info("支付金额为{}的强预约,可预约车位数: {}", appointmentOrder.getOrderAmount().toPlainString(), decr);
        //前往停车场停车的数量+1
        int i = parkCacheService.goToIncr(appointmentOrder.getParkId());
        log.info("支付成功的强预约,前往停车场停车的数量: {}", i);
        {
            //放入延时队列，到期以后如果不停车自动取消
            addDelay(appointmentOrder.getSid(), appointmentOrder.getOrderId(), appointmentOrder.getParkId());
        }
        {
            //判断是否需要订阅，第三方计费的停车场需要订阅
            subscribe(appointmentOrder.getParkId(), appointmentOrder.getPlateNumber());
        }
    }

    public UserStrokeOrderDTO createStrokeOrder(String sid, String orderId, String parkId, String plateNumber, BigDecimal amount, Integer orderType,
                                                Integer payStatus, String etcPay, Integer tripType, String userId, String nickName, LocalDateTime startTime,
                                                LocalDateTime endTime, String thirdPartyOrderId, String promotionCode) {
        return createStrokeOrder(sid, orderId, parkId, plateNumber, amount, orderType, payStatus, etcPay, tripType, userId, nickName, startTime, endTime, thirdPartyOrderId, promotionCode, orderId);
    }

    public UserStrokeOrderDTO createStrokeOrder(String sid, String orderId, String parkId, String plateNumber, BigDecimal amount, Integer orderType,
                                                Integer payStatus, String etcPay, Integer tripType, String userId, String nickName, LocalDateTime startTime,
                                                LocalDateTime endTime, String thirdPartyOrderId, String promotionCode, String bid) {
        UserStrokeOrderDTO userStrokeOrderDTO = new UserStrokeOrderDTO();
        userStrokeOrderDTO.setSid(sid);
        userStrokeOrderDTO.setTripType(tripType);
        userStrokeOrderDTO.setParkId(parkId);
        userStrokeOrderDTO.setUserId(userId);
        userStrokeOrderDTO.setPlateNumber(plateNumber);
        userStrokeOrderDTO.setOrderId(orderId);
        if (null != startTime) {
            userStrokeOrderDTO.setStartTime(startTime);
        }
        if (null != endTime) {
            userStrokeOrderDTO.setEndTime(endTime);
        }
        if (StringUtils.hasLength(nickName)) {
            userStrokeOrderDTO.setNickName(nickName);
        }
        if (StringUtils.hasLength(thirdPartyOrderId)) {
            userStrokeOrderDTO.setThirdPartyOrderId(thirdPartyOrderId);
        }
        userStrokeOrderDTO.setOrderAmount(amount);
        userStrokeOrderDTO.setPayAmount(amount);
        userStrokeOrderDTO.setOnLinePayAmount(amount);
        userStrokeOrderDTO.setPayStatus(payStatus);
        userStrokeOrderDTO.setOrderType(orderType);
        if (StringUtils.hasLength(bid)) {
            userStrokeOrderDTO.setBid(bid);
        } else {
            userStrokeOrderDTO.setBid(orderId);
        }
        userStrokeOrderDTO.setEtcPay(etcPay);
        userStrokeOrderDTO.setParkName(carParkService.idToDisplay(parkId));
        if (StringUtils.hasLength(promotionCode)) {
            userStrokeOrderDTO.setPromotionCode(promotionCode);
        }
        userStrokeOrderService.save(userStrokeOrderDTO);
        return userStrokeOrderDTO;
    }

    /**
     * 创建行程
     *
     * @param sid           行程ID
     * @param parkId        停车场ID
     * @param plateNumber   车牌号
     * @param tripType      行程状态 0停车1充电2加油
     * @param processStatus 过程状态:0生成订单未入场1预约主动取消2预约超时取消3已入场4已缴费未出场5已出场
     */
    public UserStrokeDTO createUserStroke(String sid, String parkId, String plateNumber, Integer tripType, LocalDateTime appointmentTime, String userId, BigDecimal amount, Integer processStatus, LocalDateTime intoTime) {
        UserStrokeDTO userStrokeDTO = new UserStrokeDTO();
        userStrokeDTO.setTripType(tripType);
        userStrokeDTO.setSid(sid);
        if (null != appointmentTime) {
            userStrokeDTO.setAppointmentTime(appointmentTime);
            userStrokeDTO.setStartTime(appointmentTime);
        }
        userStrokeDTO.setProcessStatus(processStatus);
        userStrokeDTO.setTripStatus(DisplayUtil.TRIP_STATUS_1);
        userStrokeDTO.setAmount(amount);
        userStrokeDTO.setPayAmount(userStrokeDTO.getAmount());
        userStrokeDTO.setOnLinePayAmount(userStrokeDTO.getAmount());
        userStrokeDTO.setParkId(parkId);
        userStrokeDTO.setUserId(userId);
        userStrokeDTO.setPlateNumber(plateNumber);
        if (null != intoTime) {
            userStrokeDTO.setIntoTime(intoTime);
        }
        return userStrokeDTO;
    }

    /**
     * 创建行程
     *
     * @param sid           行程ID
     * @param parkId        停车场ID
     * @param plateNumber   车牌号
     * @param tripType      行程状态 0停车1充电2加油
     * @param processStatus 过程状态:0生成订单未入场1预约主动取消2预约超时取消3已入场4已缴费未出场5已出场
     */
    public UserStrokeDTO createUserStroke(String sid, String parkId, String plateNumber, Integer tripType, LocalDateTime appointmentTime, String userId, BigDecimal amount, Integer processStatus) {
        return createUserStroke(sid, parkId, plateNumber, tripType, appointmentTime, userId, amount, processStatus, null);
    }


    /**
     * 创建行程
     *
     * @param sid         行程ID
     * @param parkId      停车场ID
     * @param plateNumber 车牌号
     * @param tripType    行程状态 0停车1充电2加油
     */
    public UserStrokeDTO createUserStroke(String sid, String parkId, String plateNumber, Integer tripType, LocalDateTime appointmentTime, String userId, BigDecimal amount) {
        return createUserStroke(sid, parkId, plateNumber, tripType, appointmentTime, userId, amount, DisplayUtil.PROCESS_STATUS_0);
    }

    private void subscribe(String parkId, String plateNumber) {
        final CarPark carPark = Optional.ofNullable(carParkService.getById(parkId)).orElseThrow(() -> new ApiException("停车场信息不存在"));
        /*
         * 是否第三方计费，如果是就需要调用数据中台接口，订阅停车信息
         */
        final String thirdPartyBill = Optional.ofNullable(carPark.getThirdPartyBill()).orElse(ConstantDto.SF0);
        if (thirdPartyBill.equals(ConstantDto.SF1)) {
            log.info("subscribe,parkId:{},plateNumber:{}", parkId, plateNumber);
//            dcservice.subscribe(parkId, plateNumber, false);
        }
    }

    /**
     * 是否超时
     *
     * @param billEndTime 支付结束时间
     * @param endTime     当前时间
     * @param payFreeTime 缴费后的免费时长
     * @return true or false
     */
    private boolean isTimeOut(LocalDateTime billEndTime, LocalDateTime endTime, Integer payFreeTime) {
        long minutes = Duration.between(billEndTime, endTime).toMinutes();
        return minutes > payFreeTime;
    }

    /**
     * 退款
     *
     * @param sid          行程ID
     * @param refundAmount 退款金额
     * @param now          当前时间
     */
    public void refund(String sid, BigDecimal refundAmount, LocalDateTime now, String refundDesc) {
        UserStrokeOrder userStrokeOrder = userStrokeOrderService.getLastReserve(sid);
        if (null == userStrokeOrder) {
            log.info("退款失败，订单不存在: {}", sid);
            //支付失败的，或者是0元的预约订单，这里都是没有记录的
            return;
        }
        if (refundAmount.compareTo(userStrokeOrder.getPayAmount()) > BigInteger.ONE.signum()) {
            throw new ApiException("退款金额不能大于支付金额");
        }
        refund(refundAmount, now, userStrokeOrder, refundDesc);
    }

    public void refund(BigDecimal refundAmount, LocalDateTime now, UserStrokeOrder userStrokeOrder, String refundDesc) {
        UserStrokeOrderRefundDTO query = new UserStrokeOrderRefundDTO();
        query.setOrderId(userStrokeOrder.getOrderId());
        List<UserStrokeOrderRefund> list = userStrokeOrderRefundService.getList(query);
        query.freeData();
        if (list.isEmpty()) {
            //没有退款记录，直接创建退款单退款
            createRefundOrder(userStrokeOrder, refundAmount, now, refundDesc);
        } else {
            //有退款记录的，把退款中的和退款成功过的加起来，看下总共退了多少
            //如果加上当前这笔还是小于总金额的话就可以退，否则不能退
            //是否有进行中的订单
            Money all = new Money("0");
            for (UserStrokeOrderRefund userStrokeOrderRefund : list) {
                if (userStrokeOrderRefund.getRefundStatus().equals(DisplayUtil.REFUND_STATUS_1) || userStrokeOrderRefund.getRefundStatus().equals(DisplayUtil.REFUND_STATUS_0)) {
                    all = all.add(new Money(userStrokeOrderRefund.getRefundAmount()));
                }
            }
            all = all.add(new Money(refundAmount));

            if (all.getAmount().compareTo(userStrokeOrder.getOrderAmount()) == BigInteger.ONE.signum()) {
                log.info("没有金额可以退还,总退款金额:{},orderAmount:{}", all.getAmount(), userStrokeOrder.getOrderAmount());
                throw new ApiException("没有金额可以退还");
            } else {
                createRefundOrder(userStrokeOrder, refundAmount, now, refundDesc);
            }
            list.clear();
        }
    }

    /**
     * 创建退款订单，完成退款接口调用
     *
     * @param userStrokeOrder 需要退款的订单
     * @param refundAmount    退款金额
     * @param now             退款时间
     */
    private void createRefundOrder(UserStrokeOrder userStrokeOrder, BigDecimal refundAmount, LocalDateTime now, String refundDesc) {
        boolean b = userStrokeOrderRefundService.isExistProcess(userStrokeOrder.getSid(), userStrokeOrder.getBid(), refundAmount);
        if (b) {
            log.error("当前退款已经存在，请勿重复申请,sid: {},orderId: {},refundAmount:{}", userStrokeOrder.getSid(), userStrokeOrder.getOrderId(), refundAmount);
            return;
        }
        final Integer refundNo = userStrokeOrderRefundService.getMaxNumber(userStrokeOrder.getBid());
        {
            OrderRefundDTO orderRefund = new OrderRefundDTO();
            //            if(!SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_OFFICIAL)){
//                newRefundAmount = new BigDecimal("0.01");
//            }
            orderRefund.setRefundNo(refundNo).setThirdPartyOrder(userStrokeOrder.getBid()).setNotifyUrl(pandaParkConfig.getDomain() + "/gy-panda-park/app/userStroke/notify/payAfter")
                    .setType(2).setPayAmount(userStrokeOrder.getPayAmount()).setPersonId(userStrokeOrder.getUserId()).setRefundDesc(PandaConstant.REFUND_DESC_APPOINTMENT_TIMEOUT).setRefundFee(refundAmount);
            orderHolderService.orderRefund(orderRefund);
            orderRefund.freeData();
        }

        UserStrokeOrderRefundDTO refund = new UserStrokeOrderRefundDTO();
        refund.setRefundId(IdWorker.getIdStr());
        refund.setRefundNo(refundNo);
        refund.setRefundAmount(refundAmount);
        refund.setRefundStatus(DisplayUtil.REFUND_STATUS_0);
        refund.setRefundTime(now);
        refund.setSid(userStrokeOrder.getSid());
        refund.setOrderId(userStrokeOrder.getOrderId());
        refund.setBid(userStrokeOrder.getBid());
        refund.setPayAmount(userStrokeOrder.getPayAmount());
        userStrokeOrderRefundService.save(refund);
        refund.freeData();
    }

    /**
     * 创建退款订单，完成退款接口调用
     *
     * @param bid          业务ID
     * @param orderId      订单ID
     * @param userId       用户ID
     * @param refundAmount 退款金额
     * @param now          退款时间
     */
    public void createRefundOrder(String bid, String orderId, String userId, BigDecimal refundAmount, LocalDateTime now) {
        boolean b = userStrokeOrderRefundService.isExistProcess(orderId, refundAmount);
        if (b) {
            log.error("当前退款已经存在，请勿重复申请,orderId: {},refundAmount:{}", orderId, refundAmount);
            return;
        }
        final Integer refundNo = 1;
        {
            OrderRefundDTO orderRefund = new OrderRefundDTO();
            orderRefund.setRefundNo(refundNo).setThirdPartyOrder(bid).setNotifyUrl(pandaParkConfig.getDomain() + "/gy-panda-park/app/userStroke/notify/payAfter").setType(2).setPayAmount(refundAmount)
                    .setPersonId(userId).setRefundDesc(PandaConstant.REFUND_DESC_APPOINTMENT_TIMEOUT).setRefundFee(refundAmount);
            orderHolderService.orderRefund(orderRefund);
            orderRefund.freeData();
        }

        UserStrokeOrderRefundDTO refund = new UserStrokeOrderRefundDTO();
        refund.setRefundId(IdWorker.getIdStr());
        refund.setRefundNo(refundNo);
        refund.setRefundAmount(refundAmount);
        refund.setRefundStatus(DisplayUtil.REFUND_STATUS_0);
        refund.setRefundTime(now);
        refund.setSid(null);
        refund.setOrderId(orderId);
        refund.setBid(bid);
        refund.setPayAmount(refundAmount);
        userStrokeOrderRefundService.save(refund);
        refund.freeData();
    }

    /**
     * 获得行程确认信息
     *
     * @param cid 行程确认ID
     * @return 确认信息
     */
    public UserStrokeConfirmDTO getConfirmById(String cid) {
        final UserStrokeConfirmDTO confirmDTO = Optional.ofNullable(userStrokeConfirmService.getByIdDto(cid)).orElseThrow(() -> new ApiException("行程确认不存在"));

        if (confirmDTO.getDirection().equals(PandaConstant.DIRECTION_2)) {
            final UserStrokeDTO userStrokeDTO = Optional.ofNullable(userStrokeService.getByIdDto(confirmDTO.getSid())).orElseThrow(() -> new ApiException(NULL_SID_MSG));
            confirmDTO.setUserStroke(userStrokeDTO);
            //离场才需要计算

            //未支付的，新的需要支付的
            final ArrayList<UserStrokeConfirmDTO.UserStoke> noPayList = new ArrayList<>();
            //支付成功的
            final ArrayList<UserStrokeConfirmDTO.UserStoke> payOkList = new ArrayList<>();

            //总金额
            Money amount = new Money(new BigDecimal(0));

            LocalDateTime now = LocalDateTime.now();
            final List<UserStrokeOrder> list = userStrokeOrderService.getList(confirmDTO.getSid(), false);
            UserStrokeConfirmDTO.UserStoke userStoke;
            if (list.isEmpty()) {
                userStoke = getUserStoke(userStrokeDTO, userStrokeDTO.getBillStartTime(), now);
                noPayList.add(userStoke);

                confirmDTO.setAmount(userStoke.getAmount());
                confirmDTO.setAmountDisplay(userStoke.getAmountDisplay());
                confirmDTO.setParkTime(userStoke.getParkTime());
                confirmDTO.setParkTimeDisplay(userStoke.getParkTimeDisplay());
                confirmDTO.setBillStartTime(userStoke.getBillStartTime());
                confirmDTO.setBillEndTime(userStoke.getBillEndTime());
            } else {
                final UserStrokeOrder lastOrder = list.get(0);
                userStoke = getUserStoke(userStrokeDTO, lastOrder.getEndTime(), now);
                if (userStoke.getAmount().signum() == BigInteger.ONE.signum()) {
                    //说明有需要支付的订单
                    noPayList.add(userStoke);
                }
                for (UserStrokeOrder userStrokeOrder : list) {
                    if (null == userStrokeOrder.getEndTime()) {
                        //说明没有付款
                        continue;
                    }
                    userStoke = buildUserStoke(userStrokeOrder);
                    if (userStrokeOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_1)) {
                        payOkList.add(userStoke);
                    }
                }
            }
            if (!noPayList.isEmpty()) {
                confirmDTO.setNoPayList(noPayList);
            }
            if (!payOkList.isEmpty()) {
                confirmDTO.setPayOkList(payOkList);
                for (UserStrokeConfirmDTO.UserStoke stoke : payOkList) {
                    amount = amount.add(new Money(stoke.getAmount()));
                }
            }
            BigDecimal bigDecimal = BigDecimalUtil.buildDefaultScale(amount.getAmount());
            confirmDTO.setAmount(bigDecimal);
            confirmDTO.setAmountDisplay(bigDecimal.stripTrailingZeros().toPlainString());
        }
        return confirmDTO;
    }

    private UserStrokeConfirmDTO.UserStoke buildUserStoke(UserStrokeOrder userStrokeOrder) {
        UserStrokeConfirmDTO.UserStoke userStoke;
        userStoke = new UserStrokeConfirmDTO.UserStoke();
        log.info("startTime:{},endTime:{}", userStrokeOrder.getStartTime(), userStrokeOrder.getEndTime());
        final long parkTime = Duration.between(userStrokeOrder.getStartTime(), userStrokeOrder.getEndTime()).toSeconds();
        userStoke.setParkTime(parkTime);
        userStoke.setParkTimeDisplay(DisplayUtil.displayParkTime(parkTime));
        userStoke.setBillStartTime(userStrokeOrder.getStartTime());
        userStoke.setBillEndTime(userStrokeOrder.getEndTime());
        userStoke.setAmount(userStrokeOrder.getPayAmount());
        userStoke.setAmountDisplay(BigDecimalUtil.buildDefaultScale(userStrokeOrder.getPayAmount()).toPlainString());
        userStoke.setOrderType(userStrokeOrder.getOrderType());
        userStoke.setOrderId(userStrokeOrder.getOrderId());
        return userStoke;
    }

    private UserStrokeConfirmDTO.UserStoke getUserStoke(UserStrokeDTO userStrokeDTO, LocalDateTime stat, LocalDateTime end) {
        final long parkTime = Duration.between(stat, end).toSeconds();
        //当前行程没有支付过，生成需要支付的金额，但是不生成支付订单
        BigDecimal bigDecimal = BigDecimalUtil.buildDefaultScale(calculateAmount(userStrokeDTO.getSid(), null, userStrokeDTO.getParkId(), stat, end));
        UserStrokeConfirmDTO.UserStoke userStoke = new UserStrokeConfirmDTO.UserStoke();
        userStoke.setAmount(bigDecimal);
        userStoke.setAmountDisplay(bigDecimal.stripTrailingZeros().toPlainString());
        userStoke.setParkTime(parkTime);
        userStoke.setParkTimeDisplay(DisplayUtil.displayParkTime(parkTime));
        userStoke.setBillStartTime(userStrokeDTO.getBillStartTime());
        userStoke.setBillEndTime(end);
        return userStoke;
    }

    /**
     * 验证当前登录用户绑定的车牌号码是否匹配
     *
     * @param plateNumber 前端传入的车牌号
     */
    private void checkPlateNumber(String plateNumber) {
        String userId = SecurityUtils.getLoginUser().getUserId();
        final List<UserPlateRelation> list = userPlateRelationService.getUserPlateRelationByUserId(userId);
        if (null == list || list.isEmpty()) {
            throw new ApiException("当前用户没有车牌信息");
        }
        final Optional<UserPlateRelation> optional = list.stream().filter(userPlateRelation -> userPlateRelation.getPlateNumber().equals(plateNumber)).findFirst();
        if (optional.isEmpty()) {
            throw new ApiException("当前用户绑定的车牌和传入的车牌不匹配");
        }
    }

    /**
     * 确认离场放行或者不放行
     *
     * @param now               当前时间
     * @param userStrokeConfirm 确认订单详情
     * @param userStrokeDTO     用户行程
     * @param note              备注
     */
    private void confirmOut(LocalDateTime now, UserStrokeConfirm userStrokeConfirm, UserStrokeDTO userStrokeDTO, String note) {
        userStrokeDTO.setSid(userStrokeConfirm.getSid());
        userStrokeDTO.setOutTime(now);
        userStrokeDTO.setTripStatus(DisplayUtil.TRIP_STATUS_2);
        if (StringUtils.hasLength(note)) {
            //说明是不同意的
            saveHistory(DisplayUtil.PROCESS_STATUS_6, userStrokeService.dtoToModel(userStrokeDTO), note);
        } else {
            //同意离场
            outPark(now, userStrokeDTO, note);
        }
        buildParkNum(userStrokeDTO.getSid(), userStrokeDTO.getParkId());
    }

    /**
     * 更新缓存中的预约车位数和前往停车场停车数量
     *
     * @param sid    行程ID
     * @param parkId 停车场ID
     */
    public void buildParkNum(String sid, String parkId) {
        //获得最后一笔支付成功的预约订单
        final UserStrokeOrder lastReserve = userStrokeOrderService.getLastReserve(sid);
        CarParkInfoDTO carParkInfoDTO = Optional.ofNullable(carParkService.getCarParkInfoByRedis(parkId)).orElseThrow(() -> new ApiException("停车场ID不存在,parkId： " + parkId));
        if (carParkInfoDTO.getIsAppointmentSet().equals(ConstantDto.SF1)) {
            if (null != lastReserve) {
                //确认离场，如果是预约车位，那么车位数+1
                final int incr = parkCacheService.incr(parkId);
                log.info("确认离场,预约车位数: {}", incr);
                lastReserve.freeData();
            }
            //确认离场，前往停车场停车数-1
            final int i = parkCacheService.goToDecr(parkId);
            log.info("确认离场,前往停车场停车数: {}", i);
        }
        carParkInfoDTO.freeData();
    }

    /**
     * 出场
     *
     * @param outParkTime   出场时间
     * @param userStrokeDTO 行程信息
     * @param note          备注信息，可以为空
     */
    public void outPark(LocalDateTime outParkTime, UserStrokeDTO userStrokeDTO, String note) {
        if (null == userStrokeDTO.getParkTime()) {
            LocalDateTime billEndTime = Optional.ofNullable(userStrokeDTO.getBillEndTime()).orElse(outParkTime);
            userStrokeDTO.setBillEndTime(billEndTime);
            calculatedAmount(userStrokeDTO);
            if (null != userStrokeDTO.getBillStartTime() && null != userStrokeDTO.getBillEndTime()) {
                userStrokeDTO.setParkTime(Duration.between(userStrokeDTO.getBillStartTime(), billEndTime).toSeconds());
            }
        }
        //清除最后一次行程的缓存
        userStrokeHistoryService.clearLastCache(userStrokeDTO.getUserId());
        userStrokeDTO.setOutTime(outParkTime);
        saveHistory(DisplayUtil.PROCESS_STATUS_5, userStrokeService.dtoToModel(userStrokeDTO), note);
        //确认离场生成积分
        asyncService.createIntegral(userStrokeDTO.getUserId(), null, "停车积分", PandaConstant.POINT_TYPE_PARK, userStrokeDTO.getSid());
    }

    /**
     * 确认进入放行或者不放行
     *
     * @param confirmDTO        确认信息
     * @param now               当前时间
     * @param userStrokeConfirm 确认订单详情
     * @param userStrokeDTO     用户行程
     * @param note              备注
     */
    @Transactional(readOnly = true)
    public void confirmInto(ConfirmDTO confirmDTO, LocalDateTime now, UserStrokeConfirm userStrokeConfirm, UserStrokeDTO userStrokeDTO, String note) {
        if (StringUtils.hasLength(note)) {
            //说明是不同意的
            if (null != userStrokeDTO) {
                //说明有预约信息，直接取消
                if (userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_0) || userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_7)) {
                    String orderId = Optional.ofNullable(userStrokeOrderService.getLastReserve(confirmDTO.getCid())).map(UserStrokeOrder::getOrderId).orElse(null);
                    //说明是预约单,取消预约
                    cancelReserve(confirmDTO.getCid(), orderId, DisplayUtil.PROCESS_STATUS_6, true, note);
                }
            }
        } else {
            boolean isUpdate = true;
            //同意
            if (null == userStrokeDTO) {
                userStrokeDTO = new UserStrokeDTO();
                userStrokeDTO.setSid(userStrokeConfirm.getSid());
                userStrokeDTO.setTripType(0);
                userStrokeDTO.setStartTime(now);
                userStrokeDTO.setTripStatus(DisplayUtil.TRIP_STATUS_1);
                userStrokeDTO.setParkId(userStrokeConfirm.getParkId());
                userStrokeDTO.setPlateNumber(userStrokeConfirm.getPlateNumber());
                userStrokeDTO.setUserId(userStrokeConfirm.getUserId());
                isUpdate = false;
            }
            userStrokeDTO.setIntoTime(now);
            userStrokeDTO.setBillStartTime(now);
            userStrokeDTO.setProcessStatus(DisplayUtil.PROCESS_STATUS_3);
            if (isUpdate) {
                userStrokeService.update(userStrokeDTO);
            } else {
                userStrokeService.save(userStrokeDTO);
            }
        }
    }

    /**
     * 保安强制关闭行程
     *
     * @param sid 行程ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void closeUserStroke(String sid) {
        final UserStroke userStroke = userStrokeService.getById(sid);
        if (null == userStroke) {
            return;
        }
        final Integer processStatus = userStroke.getProcessStatus();
        if (processStatus != 3) {
            //只处理状态为在停车场但是未交费的
            log.info("sid:{},processStatus:{}", sid, processStatus);
            throw new ApiException("当前状态无法处理");
        }
        LocalDateTime now = LocalDateTime.now();
        final UserStrokeDTO userStrokeDTO = userStrokeService.modelToDto(userStroke);
        calculatedAmount(userStrokeDTO);
        userStrokeDTO.setPayAmount(new BigDecimal(0));
        userStrokeDTO.setOnLinePayAmount(userStrokeDTO.getPayAmount());
        userStrokeDTO.setOutTime(now);
        saveHistory(DisplayUtil.PROCESS_STATUS_6, userStrokeService.dtoToModel(userStrokeDTO), "保安强制结束行程");

        CarParkLogDTO carParkLogDTO = new CarParkLogDTO();
        carParkLogDTO.setType(2);
        carParkLogDTO.setName(carParkService.idToDisplay(userStroke.getParkId()));
        carParkLogDTO.setNote("保安强制结束行程,门岗: " + doorpostLoginService.getDoorpostMsg(SecurityUtils.getLoginUser().getUserId()));
        carParkLogDTO.setOperatorTime(now);
        carParkLogDTO.setOperator(SecurityUtils.getLoginUser().getUserId());
        carParkLogDTO.setOrgId(SecurityUtils.getLoginUser().getCurrentOrgId());
        carParkLogDTO.setParkId(userStroke.getParkId());
        carParkLogService.save(carParkLogDTO);
        carParkLogDTO.freeData();
    }

    public String getSubPartner(String parkId) {
        CarPark carPark = Optional.ofNullable(carParkService.getById(parkId)).orElseThrow(() -> new ApiException(NULL_PARK_ID_MSG));
        OrgAgent orgAgent = Optional.ofNullable(orgAgentService.getById(carPark.getOrgId())).orElseThrow(() -> new ApiException("未设置子商户号"));
        return orgAgent.getMchId();
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> closeOrder(CloseOrderDTO closeOrder) {
        return orderHolderService.closeOrder(closeOrder);
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> refundOrder(OrderRefundDTO dto) {
        return orderHolderService.orderRefund(dto);
    }

    @Transactional(rollbackFor = Exception.class)
    public void payResult(String orderId, Integer payStatus, boolean canReserve) {
        if (payStatus.equals(DisplayUtil.PAY_STATUS_4)) {
            //取消支付，直接删除订单
            if (canReserve) {
                //预约
                userStrokeAppointmentOrderService.removeById(orderId);
                log.info("预约取消支付，删除订单，orderId: {}", orderId);
            } else {
                cancelOrder(orderId);
            }
        } else if (payStatus.equals(DisplayUtil.PAY_STATUS_3)) {
            //已支付
            if (canReserve) {
                return;
            }
            paid(orderId);

        }

    }

    /**
     * 已支付
     *
     * @param orderId 订单ID
     */
    private void paid(String orderId) {
        RLock lock = redissonClient.getLock(orderId);
        try {
            try {
                boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                if (res) {
                    UserStrokeOrder userStrokeOrder = Optional.ofNullable(userStrokeOrderService.getById(orderId)).orElseThrow(() -> new ApiException("订单不存在"));
                    if (userStrokeOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_0)) {
                        //表示状态是未支付的时候，说明还没有收到支付的状态报告，此时才能更新
                        UserStrokeOrderDTO dto = new UserStrokeOrderDTO();
                        dto.setOrderId(orderId);
                        dto.setPayStatus(DisplayUtil.PAY_STATUS_3);
                        userStrokeOrderService.update(dto);
                        dto.freeData();
                    }
                    userStrokeOrder.freeData();
                } else {
                    throw new ApiException(ApiResult.error());
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error(REDIS_LOCKED_ERR, e);
                throw new ApiException(ApiResult.error());
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 取消支付
     *
     * @param orderId 订单ID
     */
    private void cancelOrder(String orderId) {
        RLock lock = redissonClient.getLock(orderId);
        try {
            try {
                boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                if (res) {
                    UserStrokeOrder userStrokeOrder = Optional.ofNullable(userStrokeOrderService.getById(orderId)).orElseThrow(() -> new ApiException("订单不存在"));
                    if (userStrokeOrder.getPayStatus().equals(DisplayUtil.PAY_STATUS_0)) {
                        userStrokeOrderService.removeById(orderId);
                        log.info("取消支付，删除订单，orderId: {}", orderId);
                    }
                    userStrokeOrder.freeData();
                } else {
                    throw new ApiException(ApiResult.error());
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error(REDIS_LOCKED_ERR, e);
                throw new ApiException(ApiResult.error());
            }
        } finally {
            lock.unlock();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void cancelStroke(String sid) {
        if (log.isDebugEnabled()) {
            log.debug("取消行程,sid: {}", sid);
        }
        final UserStrokeDTO userStrokeDTO = Optional.ofNullable(userStrokeService.getByIdDto(sid)).orElseThrow(() -> new ApiException(NULL_SID_MSG));
        if (!userStrokeDTO.getProcessStatus().equals(DisplayUtil.PROCESS_STATUS_7)) {
            log.info("当前行程不能取消,sid： {},processStatus: {}", sid, userStrokeDTO.getProcessStatus());
            throw new ApiException("当前行程不能取消");
        }
        final UserStrokeOrder userStrokeOrder = userStrokeOrderService.getLastReserve(sid);
        String orderId = null != userStrokeOrder ? userStrokeOrder.getOrderId() : null;
        cancelReserve(sid, orderId, DisplayUtil.PROCESS_STATUS_7, true, "用户已扫码未入场,主动取消");
    }

    @Transactional(rollbackFor = Exception.class)
    public UserStrokeOrderChargeDTO chargeStartAction(String orderId) {
        UserStrokeOrderDTO userStrokeOrderDTO = Optional.ofNullable(userStrokeOrderService.getByIdDto(orderId)).orElseThrow(() -> new ApiException("订单ID不存在"));
        if (!userStrokeOrderDTO.getOrderType().equals(DisplayUtil.ORDER_TYPE_3)) {
            throw new ApiException("不是充电订单无法操作");
        }
        UserStrokeOrderChargeDTO orderCharge = userStrokeOrderChargeService.getByIdDto(orderId);
        if (null == orderCharge) {
            throw new ApiException("充电订单不存在,无法启动");
        }
        if (!orderCharge.getState().equals(DisplayUtil.CHARGE_STAT_0)) {
            log.error("无法启动,orderId: {},state: {}", orderCharge.getOrderId(), DisplayUtil.getChargeStatDisplay(orderCharge.getState()));
            throw new ApiException("无法启动");
        }
        final ChargeActionReqDTO chargeActionReqDTO = new ChargeActionReqDTO()
                .setAction(ChargeActionReqDTO.ACTION_START)
                .setStartChargeSeq(userStrokeOrderDTO.getThirdPartyOrderId())
                .setConnectorID(orderCharge.getConnectorID())
                .setOperatorID(orderCharge.getOperatorID());
        final StartChargeRespDTO startChargeRespDTO = dcService.startCharge(userStrokeOrderDTO.getParkId(), chargeActionReqDTO);
        chargeActionReqDTO.freeData();
        if (startChargeRespDTO.isSuccess()) {
            //启动充电成功，状态改为启动中
            userStrokeOrderDTO.getOrderCharge().setState(DisplayUtil.CHARGE_STAT_1);
            userStrokeOrderChargeService.update(userStrokeOrderDTO.getOrderCharge());
            //更新第三方订单号
            userStrokeOrderDTO.setThirdPartyOrderId(startChargeRespDTO.getStartChargeSeq());
            userStrokeOrderService.update(userStrokeOrderDTO);
            //此时会重新取拉去实时的充电数据
            userStrokeOrderService.buildDisplay(userStrokeOrderDTO);
            userStrokeOrderChargeService.buildDisplay(userStrokeOrderDTO.getOrderCharge());
            return userStrokeOrderDTO.getOrderCharge();
        } else {
            throw new ApiException(startChargeRespDTO.failReasonMsg());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public UserStrokeOrderChargeDTO chargeStopAction(String orderId) {
        UserStrokeOrder userStrokeOrder = Optional.ofNullable(userStrokeOrderService.getById(orderId)).orElseThrow(() -> new ApiException("订单ID不存在"));
        if (!userStrokeOrder.getOrderType().equals(DisplayUtil.ORDER_TYPE_3)) {
            throw new ApiException("不是充电订单无法操作");
        }
        UserStrokeOrderChargeDTO orderCharge = userStrokeOrderChargeService.getByIdDto(orderId);
        if (null == orderCharge) {
            throw new ApiException("充电订单不存在,无法结束");
        }
        if (!orderCharge.getState().equals(DisplayUtil.CHARGE_STAT_2) && !orderCharge.getState().equals(DisplayUtil.CHARGE_STAT_1)) {
            log.error("无法停止,orderId: {},state: {}", orderCharge.getOrderId(), DisplayUtil.getChargeStatDisplay(orderCharge.getState()));
            throw new ApiException("无法停止");
        }
        final StatusChargeRespDTO statusCharge = statusCharge(userStrokeOrder, orderCharge);

        if (!statusCharge.getStartChargeSeqStat().equals(DisplayUtil.CHARGE_STAT_2)) {
            log.error("无法停止,orderId: {},state: {}", orderCharge.getOrderId(), DisplayUtil.getChargeStatDisplay(statusCharge.getStartChargeSeqStat()));
            throw new ApiException("无法停止");
        }
        //停止
        chargeStop(userStrokeOrder, orderCharge);
        final UserStrokeOrderChargeDTO newUserStrokeOrderChargeDTO = updateUserStrokeOrder(orderCharge, statusCharge);

        orderCharge.freeData();
        userStrokeOrder.freeData();
        return newUserStrokeOrderChargeDTO;
    }

    /**
     * 更新充电订单
     *
     * @param orderCharge         充电订单
     * @param statusChargeRespDTO 查询当前实时充电信息
     * @return 更新后的充电订单
     */
    private UserStrokeOrderChargeDTO updateUserStrokeOrder(UserStrokeOrderChargeDTO orderCharge, StatusChargeRespDTO statusChargeRespDTO) {
        if (null != statusChargeRespDTO) {
            if (null != statusChargeRespDTO.getTotalMoney()) {
                orderCharge.setTotalAmount(new Money(statusChargeRespDTO.getTotalMoney()).getAmount());
            }
            if (null != statusChargeRespDTO.getElectricityMoney()) {
                orderCharge.setElectricityFee(new Money(statusChargeRespDTO.getElectricityMoney()).getAmount());
            }
            if (null != statusChargeRespDTO.getTotalPower()) {
                orderCharge.setChargeCapacity(new Money(statusChargeRespDTO.getTotalPower()).getAmount());
            }
            if (null != statusChargeRespDTO.getServiceMoney()) {
                orderCharge.setServiceCharge(new Money(statusChargeRespDTO.getServiceMoney()).getAmount());
            }
            if (null != statusChargeRespDTO.getStartChargeSeqStat()) {
                orderCharge.setState(statusChargeRespDTO.getStartChargeSeqStat());
            }
            if (null != statusChargeRespDTO.getSoc()) {
                orderCharge.setSoc(statusChargeRespDTO.getSoc());
            }
        }
        //设置状态为停止中
        orderCharge.setState(DisplayUtil.CHARGE_STAT_3);
        return userStrokeOrderChargeService.update(orderCharge);
    }

    /**
     * 停止充电
     *
     * @param userStrokeOrder 订单信息
     * @param orderCharge     充电订单信息
     */
    private void chargeStop(UserStrokeOrder userStrokeOrder, UserStrokeOrderChargeDTO orderCharge) {
        final ChargeActionReqDTO chargeActionReqDTO = new ChargeActionReqDTO()
                .setAction(ChargeActionReqDTO.ACTION_STOP)
                .setStartChargeSeq(userStrokeOrder.getThirdPartyOrderId())
                .setConnectorID(orderCharge.getConnectorID())
                .setOperatorID(orderCharge.getOperatorID());
        dcService.stopCharge(userStrokeOrder.getParkId(), chargeActionReqDTO);
        chargeActionReqDTO.freeData();
    }

    /**
     * 查询充电状态
     *
     * @param userStrokeOrder 订单信息
     * @param orderCharge     充电订单信息
     * @return 充电实时数据
     */
    private StatusChargeRespDTO statusCharge(UserStrokeOrder userStrokeOrder, UserStrokeOrderChargeDTO orderCharge) {
        final ChargeActionReqDTO chargeActionReqDTO = new ChargeActionReqDTO()
                .setAction(ChargeActionReqDTO.ACTION_STATUS)
                .setStartChargeSeq(userStrokeOrder.getThirdPartyOrderId())
                .setConnectorID(orderCharge.getConnectorID())
                .setOperatorID(orderCharge.getOperatorID());
        final StatusChargeRespDTO statusCharge = dcService.statusCharge(userStrokeOrder.getParkId(), chargeActionReqDTO);
        chargeActionReqDTO.freeData();
        return statusCharge;
    }
}